How does GHC interpret `foldl + 0 [1,2,3]` (without parentheses around +)? - haskell

One thing that got me stuck early when learning Haskell was the difference between foldl + and foldl (+).
Prelude> :t foldl + 0 [1,2,3]
:: (Num t1, Num ((b -> a -> b) -> b -> t a -> b),
Num ([t1] -> (b -> a -> b) -> b -> t a -> b), Foldable t) =>
(b -> a -> b) -> b -> t a -> b
vs
Prelude> :t foldl (+) 0 [1,2,3]
foldl (+) 0 [1,2,3] :: Num b => b
How does Haskell / GHC derive the type of foldl + 0 [1,2,3]? How can I understand why it expands to this giant type?

Because + is an infix operator and there are no parentheses overriding things,
foldl + 0 [1,2,3]
parses as
(foldl) + (0 [1,2,3])
The easiest starting point is the type of foldl, which is well known (and if you don't know it, you can just ask GHCI with :t foldl).
foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a
Next, the other side of the addition. Because 0 is being applied as a function with [1,2,3] as an argument, there must be a Num instance for a function type which takes a list of some numeric type as input, and produces as output...well, we'll get to that.
0 [1,2,3] :: (Num t, Num ([t] -> s)) => s
Because + is being applied to these two expressions, they must have the same type. Therefore, we must unify
foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a
with
0 [1,2,3] :: (Num t, Num ([t] -> s)) => s
The most general way to do that is to let s be the exact same type as foldl (combining their constraints), giving us
0 [1,2,3] :: (Foldable f,
Num t,
Num ([t] -> (a -> b -> a) -> a -> f b -> a))
=> (a -> b -> a) -> a -> f b -> a
And remember that of course foldl must have precisely the same type:
foldl :: (Foldable f,
Num t,
Num ([t] -> (a -> b -> a) -> a -> f b -> a))
=> (a -> b -> a) -> a -> f b -> a
And since + is from the Num typeclass, the type that they share must be Num as well.
foldl + 0 [1,2,3] :: (Foldable f,
Num t,
Num ([t] -> (a -> b -> a) -> a -> f b -> a),
Num ((a -> b -> a) -> a -> f b -> a))
=> (a -> b -> a) -> a -> f b -> a
Which as you can see is, modulo some renaming of types, just what GHC told you.
But of course, this is a rather silly type. It's possible someone would write all these outrageous Num instances, so GHC dutifully infers this as a valid type. But nobody actually has written these instances, so you will have a great deal of trouble actually using this expression. Really what you should do is fix your parentheses.

foldl + 0 [1,2,3] is parsed as the sum of foldl and 0 [1,2,3], which is 0 applied to the list [1,2,3].

In Haskell parentheses are not only used to set precedence of expression evaluation. The two statements are actually quite different.
The sign + is an operator, so it's supposed to work like a + b = c. That is, one function argument to the left and the other to the right. You say the operator is used in infix notation when it is like that.
You can covert any operator to a normal function being in the prefix notation by having parentheses. (+) a b = c.
Because of this, your two provided expressions are quite different. It's natural they have different type signatures.

Related

Arguments not needed when using foldl in haskell?

What I don't get is how it is possible to use foldl in this way in haskell. I do not understand how the argument ( in this case list) is carried over implicitly:
addAll :: [Int] -> Int
addAll = foldl (+) 0
-- This is how I could write foldl to simplify addAll where xs is clearly defined
addAll :: [Int] -> Int
addAll xs = foldl (+) 0 xs
or
addAll :: [Int] -> Int
addAll = \ xs -> foldl (+) 0 xs
But I don't really understand the first example. So basically I wonder how it is possible for something to be evaluated like that in haskell?
But I don't really understand the first example. So basically I wonder how it is possible for something to be evaluated like that in haskell?
The foldl (+) 0 produces a function. A function of type (Foldable f, Num a) => f a -> a, so why would you need an extra parameter? The addAll is a function as well.
In functional programming, functions are "first class citizens". This means that you can pass functions as parameters, and that the result can be a function. In Haskell every function takes exactly one parameter. Indeed:
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
is short for:
foldl :: Foldable t => (b -> a -> b) -> (b -> (t a -> b))
foldl is thus a function that takes as parameter a function of type (b -> a -> b), and produces a function of type b -> t a -> b. This thus means that foldl (+) has type:
foldl (+) :: (Foldable f, Num b) => b -> (f b -> b)
again a function that in this case takes a parameter the base case for foldl, and returns then a function that maps a (Foldable f, Num a) => f a -> f a. If you write foldl (+) 0, this is thus short for (fold (+)) 0.
Remember that all functions in Haskell are curried. foldl is no exception; it has type Foldable t => (b -> a -> b) -> b -> t a -> b, which means it takes an argument of type (b -> a -> b) and returns a function of type Foldable t => b -> t a -> b.
That function is also curried. foldl (+) takes an argument of type Int b => b and returns a function of type (Foldable t, Num b) => t b -> b.
Thus, foldl (+) 0 has type (Foldable t, Num b) => t b -> b, and your type annotation makes the restrictions t ~ [] and b ~ Int.

Haskell function with type (num -> num) -> num

I am struggling with an exercise in R. Bird's functional programming book that asks for an example of a function with type (num -> num) -> num
The best I can come up with is a polymorphic type
func1 f = f 3
:t func1
func1 :: Num t1 => (t1 -> t2) -> t2
The problem I am having is that I can't specify the return type of f, so the type remains (num -> t2) -> t2.
My attempt to force the return type of f is as follows:
square x = x * x
:t func1 square
func1 square :: Num t2 => t2 -> t2
Because of course if I try to find the type of func1 ∘ square it will just be num -> num
If it is enough to give a function which can be assigned that type, then yours is already enough. That is, the following type-checks just fine:
func1 :: Num a => (a -> a) -> a
func1 f = f 3
If, on the other hand, you want a function which is inferred to have that type, then you need to do some trickery. What we want to do here is to specify that the result of f 3 and the 3 that we fed in have the same type. The standard way to force two terms to have the same type is to use asTypeOf, which is implemented this way:
asTypeOf :: a -> a -> a
asTypeOf x _ = x
So let's try:
> :t \f -> f 3 `asTypeOf` 3
(Num a, Num t) => (t -> a) -> a
Unfortunately for us, this doesn't work, because the 3 in f 3 and the standalone 3 are inferred to be using potentially different instances of Num. Still, it is a bit closer than \f -> f 3 was -- note the new Num a constraint on the output that we didn't have before. An obvious next idea is to let-bind a variable to 3 and reuse that variable as the argument to both f and asTypeOf; surely then GHC will get the picture that f's argument and result have the same type, right?
> :t \f -> let x = 3 in f x `asTypeOf` x
(Num a, Num t) => (t -> a) -> a
Drat. Turns out that lets do what's called "let generalization"; the x will be just as polymorphic as the 3 was, and can be specialized to different types at different use sites. Usually this is a nice feature, but because we're doing an unnatural exercise we need to do unnatural things...
Okay, next idea: some lambda calculi do not include a let, and when you need one, instead of writing let a = b in c, you write (\a -> c) b. This is especially interesting for us because Haskell uses a specially-restricted kind of polymorphism that means that inside c, the type of a is monomorphic. So:
> :t \f -> (\x -> f x `asTypeOf` x) 3
Num a => (a -> a) -> a
And now you complain that asTypeOf is cheating, because it uses a type declaration that doesn't match its inferred type, and the whole point of the exercise was to get the right type through inference alone. (If we were okay with using type declarations that don't match the inferred type, we could have stopped at func1 :: Num a => (a -> a) -> a; func1 f = f 3 from way back at the beginning!) Okay, no problem: there's another standardish way to force the types of two expressions to unify, namely, by putting them in a list together. So:
> :t \f -> (\x -> head [f x, x]) 3
Num a => (a -> a) -> a
Phew, now we're finally at a place where we could in principle build, from the ground up, all the tools needed to get a term of the right type without any type declarations.
func1 f = let x = f x in x This is a partial function, it technically has the type you want and you should be aware of what they are and how they work in haskell.

Fold type signature with `Foldable` type constraint

On my GHCi foldr and foldl have this signature:
Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
What is the difference between the alternative type signature of fold, specifically t a part?
fr :: (a -> b -> b) -> b -> [a] -> b
fl :: (b -> a -> b) -> b -> [a] -> b
The type class Foldable defines a number of functions in addition to foldl.
class Foldable t where
foldMap :: Monoid m => t m -> m
foldr :: (a -> b -> b) -> b -> t a -> b
foldl :: (b -> a -> b) -> b -> t a -> b
-- ...
We can define an instance of Foldable for a type constructor t by defining at least foldMap or foldr for the type. (foldl has a default definition in terms of some of the others, so you get it for "free" as long as you supply the minimum definition).
instance Foldable [] where
foldr f b [] = b
foldr f b (x:xs) = f x : foldr f b xs
instance Foldable Maybe where
foldr f b Nothing = b
foldr f b (Just a) = f b a
The t in the type signature just means that as long as a type constructor has a Foldable instance for it defined, you can use foldl with a value of the appropriate type. For example:
Prelude> foldl (+) 0 [1,2,3]
6
Prelude> foldl (+) 0 []
0
Prelude> foldl (+) 0 Nothing
0
Prelude> foldl (+) 0 (Just 3)
3
In the first two cases, we use the [] instance because the arguments corresponding to t a are lists, meaning t is unified with [], written t ~ []. In the second two, we use the Maybe instance because the arguments corresponding to t a are Maybe values, so t ~ Maybe.

Comparing fmap type declaration to how it's used

I ran :t fmap (*) (Just 5) and got
fmap (*) (Just 5) :: Num a => Maybe (a -> a)
Type declaration of fmap says
fmap :: Functor f => (a -> b) -> f a -> f b
So am I right in thinking that (*) is being treated as (a -> (a -> a)) and corresponds to (a -> b) part of the fmap type declaration? If the (a -> a) part corresponds to b, why can't I do the following? And let the b represent the lambda function?
foo :: a -> b
foo x = (\y -> y + 3)
There's an implicit forall in your signature:
foo :: forall a b. a -> b
This means that your function must work for any a and b. Let's have a look at your function:
foo x = (\y -> y + 3)
Since x isn't restricted on the right hand side of =, it fulfils the requirements: it can be of any type. However, your right hand side has the following type:
ghci> :t (\y -> y + 3)
Num k => k -> k
Remember that your original function said that the choice of b can be arbitrary. However, your function's body restricts the return type to b ~ Num k => k -> k. You would have to use
foo :: Num b => a -> b -> b
By the way, foo's type already shows that you will not use the first argument, because there's no way to combine two values of different types in a general way. Indeed, one can proof that foo is const bar for some bar :: Num b => b -> b.
Exercises
Try to think of a function with type forall a b. a -> b.
In your initial example, a is constrained to be an instance of Num because of the 5 and the (*).
:type 5
5 :: Num a => a
:type (*)
(*) :: Num a => a -> a -> a
You are correct that the (a->a) gets unified with the b in the type of fmap.
:type (\y -> y + 3)
(\y -> y + 3) :: Num a => a -> a
Hence:
:type foo
foo :: Num a => t -> a -> a
As Willem Van Onsem says, b means "any type you like", not "any type that fits the invisible constraints I haven't mentioned".

What is the justification for the type of unfoldr in Haskell?

The example given in the documentation of unfoldr :: (b -> Maybe (a, b)) -> b -> [a]:
unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
can easily be written with a redundant pair:
unfoldr (\b -> if b == 1 then Nothing else Just (b-1, b-1)) 11
What does unfoldr need the pair (a,b) for? Why is its type not (a -> Maybe a) -> a -> [a]?
A function with type
(a -> Maybe a) -> a -> [a]
restricts the output list element type to be the same as the state which is threaded through the generation process. unfoldr is more general in that it allows an independent type of state to be used.
Leveraging a bit of theory, one can recover the types for folds/unfolds of a recursive type, including lists, understanding why they are what they are.
Let A be a fixed type. The type "list of As" satisfies the isomorphism
List ~~ Either () (A, List)
which can be read "a list value is either a special value (the empty list), or a value of type A followed by a list value".
In a more succinct notation we could write Either as an infix +:
List ~~ () + (A, List)
Now, if we let F b = () + (A, b), we have that
List ~~ F List
The above is a fixed point equation, which always arises when using recursive types. For any recursive type defined by T ~~ F T we can
derive the type of the related folds/unfolds (also known as cata/ana or induction/coinduction principles)
fold :: (F b -> b) -> T -> b
unfold :: (b -> F b) -> b -> T
In the case of lists, we then obtain
fold :: ((() + (A, b)) -> b) -> List -> b
unfoldr :: (b -> (() + (A, b))) -> b -> List
The unfold can also be rewritten noting that Maybe c ~~ () + c:
unfoldr :: (b -> Maybe (A, b)) -> b -> List
The fold can instead be rewritten using
((x + y) -> z) ~~ (x -> z, y -> z)
getting
foldr :: (() -> b, (A, b) -> b) -> List -> b
then, since () -> b ~~ b,
foldr :: (b, (A, b) -> b) -> List -> b
finally, since (x, y) -> z ~~ x -> y -> z (currying), we have
foldr :: b -> ((A, b) -> b) -> List -> b
and again:
foldr :: b -> (A -> b -> b) -> List -> b
and with a final flip x -> y -> z ~~ y -> x -> z:
foldr :: (A -> b -> b) -> b -> List -> b
How do those types follow from the (co)induction principles?
Domain theory states that least fixed points (F(T)=T) coincide with least
prefixed points (F(T)<=T), when F is monotonic over a certain poset.
The induction principle simple states that, if T is the least prefixed point, and F(U)<=U, then T<=U. (Proof. It is the least!. QED.)
In formulae,
(F(U) <= U) ==> (T <= U)
To deal with fixed points over types, we need to switch from posets to categories, which makes everything more complex. Very, very roughly, every <= is replaced with some morphism. For instance, F(U)<=U now means that there is some morphism F U -> U. "Monotonic F" means that F is a functor (since a<=b implying F(a)<=F(b) now becomes (a->b) implying F a -> F b). Prefixed points are F-algebras (F u -> u). "Least" becomes "initial". And so on.
Hence the type of fold: (implication is -> as well)
fold :: (F u -> u) -> (T -> u)
Unfold derives from the coinduction principle, which deals with greatest postfix points T (which become coalgebras)
(U <= F(U)) ==> (U <= T)

Resources