1 + 1 -- Int plus 1 + "1" -- Type error: "String is not an instance of Num" "1" + [] -- Type error
Fun with Numbers
Many programming languages - especially the "practical" ones - have their weird corner cases and inconsistencies when dealing with numbers as nicely pointed out by this xkcd comic.
This comes to a large extend from using numerical operators with non-numerical values
(think +
for String concatenation) and automatic coercion
between numbers and Strings. These issues do not occur in Frege (nor in Haskell) where
operators never work on a mixture of types.
However, there is some clever inference going on at compile time to make a programmer’s life easier. The type system would in principle not allow to write
1 + 1.0
because 1
is an Int
and 1.0
is a Double
and +
requires both operands
to be of the same type.
The compiler is smart enough to not insist on you writing 1.0 + 1.0
for this
simple case of number literals. It sees what you meant by resolving the
type constraints in the expression that 1
is used in.
Let’s see another example that is a just a bit more advanced:
1 / 2
Again, 1
and 2
would be Int
literals but the division operator is not
defined for the Int
type (or any other Integral type) in Frege.
It requires a number type of the Real
typeclass for both operands and Frege chooses the concrete type Double
for you in this case (unlike Haskell, this default is fix).
It follows:
1 / 2 == 0.5 -- type is Double
Note
|
This is very much unlike Java where 1/2 evaluates to 0 .
|
This has some interesting consequences since the Java Double
type
handles many special cases in a nice manner.
Double is nice
The most common special case is division by zero. How is this handled in Frege?
1 / 0 -- Infinity
Warning
|
The String representation of a number is not always made up from
digits only. Developers are usually aware that there are dots, minus, and the E character
to consider but they often forget about Infinity and NaN (not a number).
|
And how do we calculate with Infinity? Well, when you add to infinity, the result is equally infinite. You can even add infinities to infinities. Division is not defined, though.
(1/0) + 1 -- Infinity (1/0) * 3 -- Infinity (1/0) + (1/0) -- Infinity (1/0) * (1/0) -- Infinity (1/0) / (1/0) -- NaN
And what do we gain from having Infinity
as opposed to NaN
?
Well, with Infinity
you have a chance of coming back to numbers that you
can do normal calculations with:
1 / (1/0) -- 0.0
Wow, this is now definitely interesting and makes us ask for other corner cases of mathematics:
0 / 0 -- NaN (1/0) / 0 -- Infinity 0 ^ 0 -- 1
This looks like we have violated some rules of mathematics.
Have we broken math?
In mathematics, division by zero is undefined. Likewise is division by infinity or zero to the power of zero.
However, computers calculate with approximations, they do applied numeric math at best.
We use Math.PI
and Math.E
with limited precision. Heck, even square roots are only
approximations:
import frege.prelude.Math Math.sqrt 2 * Math.sqrt 2 -- 2.0000000000000004
And given that we have to deal with limited precision anyway, we can just as well take liberty to make operations like division by zero not blowing up our programs but behaving reasonably well within computational bounds.
References
XKCD | |
Numberphile on zero |