Remix.run Logo
mwkaufma 4 days ago

Big "college freshman" energy in this take:

  I personally prefer to make the error state part of the objects: Streams can be in an error state, floats can be NaN and integers should be low(int) if they are invalid (low(int) is a pointless value anyway as it has no positive equivalent).
It's fine to pick sentinel values for errors in context, but describing 0x80000000 as "pointless" in general with such a weak justification doesn't inspire confidence.
ratmice 5 hours ago | parent | next [-]

Without the low int the even/odd theorem falls apart for wrap around I've definitely seen algorithms that rely upon that.

I would agree, whether error values are in or out of band is pretty context dependent such as whether you answered a homework question wrong, or your dog ate it. One is not a condition that can be graded.

umanwizard 4 hours ago | parent [-]

What is the "even/odd theorem" ?

ratmice 3 hours ago | parent [-]

that all integers are either even or odd, and that for an even integer that integer + 1 and - 1 are odd and vice versa for odd numbers. That the negative numbers have an additional digit from the positive numbers ensures that low(integer) and high(integer) have different parity. So when you wrap around with overflow or underflow you continue to transition from an even to odd, or odd to even.

xigoi 3 hours ago | parent [-]

If you need wraparound, you should not use signed integers anyway, as that leads to undefined behavior.

ratmice 3 hours ago | parent | next [-]

Presumably since this language isn't C they can define it however they want to, for instance in rust std::i32::MIN.wrapping_sub(1) is a perfectly valid number.

xigoi 3 hours ago | parent [-]

Nim (the original one, not Nimony) compiles to C, so making basic types work differently from C would involve major performance costs.

umanwizard 3 hours ago | parent | prev [-]

Signed overflow being UB (while unsigned is defined to wrap) is a quirk of C and C++ specifically, not some fundamental property of computing.

Symmetry 2 hours ago | parent | next [-]

Specifically, C comes form a world where allowing for machines that didn't use 2's compliment (or 8 bit bytes) was an active concern.

aw1621107 2 hours ago | parent [-]

Interestingly, C23 and C++20 standardized 2's complement representation for signed integers but kept UB on signed overflow.

xigoi 3 hours ago | parent | prev [-]

Nim (the original one, not Nimony) compiles to C, so making basic types work differently from C would involve major performance costs.

ratmice 3 hours ago | parent | next [-]

Presumably unsigned want to return errors too?

Edit: I guess they could get rid of a few numbers... Anyhow it isn't a philosophy that is going to get me to consider nimony for anything.

umanwizard 3 hours ago | parent | prev [-]

> making basic types work differently from C would involve major performance costs.

Not if you compile with optimizations on. This C code:

  int wrapping_add_ints(int x, int y) {
      return (int)((unsigned)x + (unsigned)y);
  }
Compiles to this x86-64 assembly (with clang -O2):

  wrapping_add_ints:
          lea     eax, [rdi + rsi]
          ret
Which, for those who aren't familiar with x86 assembly, is just the normal instruction for adding two numbers with wrapping semantics.
sevensor 5 hours ago | parent | prev [-]

I have been burned by sentinel values every time. Give me sum types instead. And while I’m piling on, this example makes no sense to me:

    proc fib[T: Fibable](a: T): T =
      if a <= 2:
        result = 1
      else:
        result = fib(a-1) + fib(a-2)
Integer is the only possible type for T in this implementation, so what was the point of defining Fibable?
Hendrikto 5 hours ago | parent | next [-]

I agree about sentinel values. Just return an error value.

I think the fib example is actually cool though. Integers are not the only possible domain. Everything that supports <=, +, and - is. Could be int, float, a vector/matrix, or even some weird custom type (providing that Nim has operator overloading, which it seems to).

May not make much sense to use anything other than int in this case, but it is just a toy example. I like the idea in general.

sevensor 4 hours ago | parent [-]

Well, I agree about Fibable, it’s fine. It’s the actual fib function that doesn’t work for me. T can only be integer, because the base case returns 1 and the function returns T. Therefore it doesn’t work for all Fibables, just for integers.

cb321 3 hours ago | parent | next [-]

In this case, it compiles & runs fine with floats (if you just delete the type constraint "Fibable") because the string "1" can be implicitly converted into float(1) { or 1.0 or 1f64 or float64(1) or 1'f64 or ..? }. You can think of the "1" and "2" as having an implicit "T(1)", "T(2)" -- which would also resolve your "doesn't work for me" if you prefer the explicitness. You don't have to trust me, either. You can try it with `echo fib(7.0)`.

Nim is Choice in many dimensions that other PLang's are insistently monosyllabic/stylistic about - gc or not or what kind, many kinds of spelling, new operator vs. overloaded old one, etc., etc., etc. Some people actually dislike choice because it allows others to choose differently and the ensuing entropy creates cognitive dissonance. Code formatters are maybe a good example of this? They may not phrase opposition as being "against choice" as explicitly as I am framing it, but I think the "My choices only, please!" sentiment is in there if they are self-aware.

sevensor 2 hours ago | parent [-]

But given the definition of Fibable, it could be anything that supports + and - operators. That could be broader than numbers. You could define it for sets for example. How do you add the number 1 to the set of strings containing (“dog”, “cat”, and “bear”)? So I suppose I do have a complaint about Fibable, which is that it’s underconstrained.

Granted, I don’t know nim. Maybe you can’t define + and - operators for non numbers?

cb321 an hour ago | parent [-]

Araq was probably trying to keep `Fibable` short for the point he was trying to make. So, your qualm might more be with his example than anything else.

You could add a `SomeNumber` predicate to the `concept` to address that concern. `SomeNumber` is a built-in typeclass (well, in `system.nim` anyway, but there are ways to use the Nim compiler without that or do a `from system import nil` or etc.).

Unmentioned in the article is a very rare compiler/PLang superpower (available at least in Nim 1, Nim 2) - `compiles`. So, the below will print out two lines - "2\n1\n":

    when compiles SomeNumber "hi": echo 1 else: echo 2
    when compiles SomeNumber 1.0: echo 1 else: echo 2
Last I knew "concept refinement" for new-style concepts was still a work in progress. Anyway, I'm not sure what the most elegant way to incorporate this extra constraint, but I think it's a mistake to think it is unincorporatable.

To address your question about '+', you can define it for non-SomeNumber, but you can also define many new operators like `.+.` or `>>>` or whatever. So, it's up to your choice/judgement if the situation calls for `+` vs something else.

Hendrikto 3 hours ago | parent | prev [-]

I see, I misunderstood your complaint then.

However, the base case being 1 does not preclude other types than integers, as cb321 pointed out.

treeform 2 hours ago | parent | prev [-]

There can be a lot of different integers, int16, int32 ... and unsigned variants. Even huge BigNum integers of any lengths.