I used this Haskell interpreter right here: https://tryhaskell.org/
When I provided the input 5 1
it tells me - I assume it is the type - that the expression is of type (Num a, Num (a -> t)) => t
:
λ 5 1
:: (Num a, Num (a -> t)) => t
Now I have tried to understand how to interpret this and this is what I came up with.
a
is any Num
type and so is a -> t
. The expression results in some type t
that is produced by theoretically applying 1
(type a
) to 5
(type a -> t
).
This really disturbs me because I don't see how the constraint Num (a -> t)
makes sense. Theoretically it looks to be the correct interpretation but I can't find prove for that.
It makes sense because Num
is a polymorphic type - which can be a function.
Numerals are polymorphic in Haskell. When you write something like
5
The compiler turns this into fromInteger (5 :: Integer) :: Num a => a
. The compiler doesn't know beforehand what instance to use, so it'll do it's best to guess from context. If you have 5 1
, then the first numeral is a polymorphic Num
type, that also must be a function. The second numeral is just seen as a Num
, so this is quite normal.
But you may ask "How can a number be a function?" If everything in the world made sense, then numbers wouldn't be functions, but you can actually write an instance for them that behaves, more or less:
{-# LANGUAGE FlexibleInstances #-}
instance Num a => Num (a -> a) where
fromInteger a = const (fromInteger a)
a + b = \c -> a c + b c
a * b = \c -> a c * b c
abs a = \c -> abs (a c)
signum a = \c -> signum (a c)
negate a = \c -> negate (a c)
This satisfies the definition, but it probably wouldn't be very useful. For example:
> let x = 1 :: Int -> Int; y = 2 :: Int -> Int
> x + y $ 0
3
> x + y $ 102089
3
> x + y $ undefined
3
So here, the argument does not matter to the expression, it's not even evaluated. How about a more interesting one:
> let x = (+10); y = (*10)
> x + y $ 0 -- x 0 + y 0 = 0 + 10 + 0 * 10
10
> x + y $ 1 -- x 1 + y 1 = 1 + 10 + 1 * 10
21
> x + y $ 2
32
And so forth. I'm sure someone can find an interesting use case for it, but I wouldn't recommend it, it's quite clearly not very intuitive.
In Haskell, numbers are polymorphic:
λ> :t 5
5 :: Num a => a
The important thing to note here is that function application in Haskell is whitespace. So to make something like 5 2
typecheck, all you have to do is create an instance for (a -> a)
type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With