My question is about how to work with Haskell type signatures analytically. To make it concrete, I'm looking at the "fix" function:
fix :: (a -> a) -> a
and a little made-up function that I wrote to do Peano-ish addition:
add = \rec a b -> if a == 0 then b else rec (a-1) (b+1)
When I examine the types, I get my expected type for fix add
:
fix add :: Integer -> Integer -> Integer
And it seems to work like I'd expect:
> (fix add) 1 1
2
How can I work with the type signatures for fix
and for add
to show that fix add
has the above signature? What are the "algebraic", if that's even the right word, rules for working with type signatures? How could I "show my work"?
ghci
tells us
add :: Num a => (a -> a -> a) -> a -> a -> a
modulo some typeclass noise since the second argument to add
requires an Eq
instance (you're checking it for equality with 0
)
When we apply fix
to add
, the signature for fix
becomes
fix :: ((a -> a -> a) -> (a -> a -> a)) -> (a -> a -> a)
Remember, the a
s in fix :: (a -> a) -> a
can have any type. In this case they have type (a -> a -> a)
Thus fix add :: Num a => a -> a -> a
, which is exactly the right type to add two a
s.
You can work with Haskell's type signatures in a very algebraic fashion, variable substitution works just like you'd expect. In fact, theres a direct translation between types and algebra.
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