Comment by 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.

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.

  • What is the "even/odd theorem" ?

    • 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.

      9 replies →

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?

  • 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.

    • 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.

      4 replies →

  • You're completely missing the point of this casual example in a blog post ... as evidenced by the fact that you omitted the type definition that preceded it, that is the whole point of the example. That it's not the best possible example is irrelevant. What is relevant is that the compiler can type check the code at the point of definition, not just at the point of instantiation.

    And FWIW there are many possible types for T, as small integer constants are compatible with many types. And because of the "proc `<=`(a, b: Self): bool" in the concept definition of Fibable, the compiler knows that "2" is a constant of type T ... so any type that has a conversion proc for literals (remember that Nim has extensive compile-time metaprogramming features) can produce a value of its type given "2".

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