How to analyse a function in Haskell? - haskell

Hi everyone Haskell newbie here. I'm really confused about how to look at a curried function.
For example, here is a function definition
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
When I call
zipWith' (zipWith' (*)) [[1,2,3],[3,5,6]] [[3,2,2],[3,4,5]]
I understand that
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
is equivalent to
zipWith' :: (a -> b -> c) -> ([a] -> ([b] -> [c]))
But what I don't understand is the way/order to look at it. Should we analyse it from left to right? Such as look at the (a -> b -> c) first and then apply the [a] in to the (a -> b -> c) and lastly apply ([b] -> [c]) into (a -> b -> c) -> [a]? Or is it the other way round?
And if you don't understand what I'm asking (sorry :( ), could you just show me how you think when you see this kind of problems? I freak out whenever I see these functions and it usually takes me a while to figure out :P

Curried functions look scarier than they are. One way of thinking about how they work is uncurrying them. For instance:
(++) :: [t] -> [t] -> [t]
Becomes, when uncurried:
(++) :: ([t], [t]) -> [t]
In my eyes, that's all the explanation needed, but here's a more detailed one, that might explain it fully:
A function of type a -> b takes an argument of type a and returns an argument of type b. Remember that a -> b is in itself a concrete type and could take the place of b in the first, making a function of something like a -> (b -> c).
Let's say that a function f is of type a -> b -> c, which is as above. What does that mean? It is a function that returns a function! That is how currying works. The idea of functions returning other functions allows us to partially apply functions, limiting or specifying their traits.
Let's now make our own function specifying that:
func :: a -> [a] -> [a]
func = \elem -> ( \list -> elem : list )
The lambda expressions here have been written in such a way that it should be obvious what the type means: the first lambda (\elem -> ...) constructs the other based on it's input.
Now, let's have a look at zipWith:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
The first argument to zipWith is a function, which in itself takes two arguments. Not too crazy.
The second argument is a list which can be passed to the function, because of the type variables defined.
The third argument is another list that can be passed to the function after the first.
The return value is a list of the resultant type of the function we passed to zipWith first.

So, let's start with zipping function. It's type is a -> (b -> c). It takes as a parameter of type a, and returns function which transforms b to c. It is isomorphic to it's uncurried equivalence (a, b) -> c, the difference is that we fix first parameter, to get function which now requires only b to return c.
So this is what the zipper's type mean. Now let's move on to zipWith'. It takes as a parameter zipping function (explained above). It returns something of type [a] -> ([b] -> [c]), which is pretty similiar to zipper function. We can say that zipWith' lifts zipping function to work with lists -zipWith' returns a function witch takes a list of as, and returns function which takes list of bs and returns list of cs.
In your example we have zipWith' (*). For simplicity assume that (*) is of type Int -> (Int -> Int) (in reality it is more general - (Num a) => a -> (a -> a). Then we have a ~ b ~ c ~ Int and zipWith' (*) :: [Int] -> ([Int] -> [Int]). Then zipWith' (*) takes as a parameter a list of Ints and returns a function which transforms one list of Ints to another. So it can be used to zip two lists of lists of as. And this is exactly what happens.

In this case it might be helpful to think of the nested lists as (2-dimensional) matrices.
A = ( 1 2 3 ) B = ( 3 2 2 ) --> result = ( 3 4 6 )
( 3 5 6 ) ( 3 4 5 ) ( 9 20 30 )
The outer zip' combines the rows from A and B and the inner zip combines a pair of rows into another row, and so it is clear the result will have the same shape as the inputs.
It also might be helpful to realize that zipWith (*) is just component-wise multiplication of vectors. In fact there is a pattern:
zipWith (*) -- component-wise * on 1-d matrices
zipWith (zipWith (*)) -- component-wise * on 2-d matrices
zipWith (zipWith (zipWith (*))) -- component-wise * on 3-d matrices
...
and of course (*) can be replaced by any binary operation.
Some other list functions and their matrix interpretations:
map - iterate over all of the rows
transpose - transpose rows and columns
concat - flatten the first dimension (i.e. all rows into a single row)
As an exercise you might try implementing matrix multiplication with list operations.

Left to right.
In fact you can see it with 4 different point of view
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
Or right to left :-)
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list

Related

Use anonymous function as parameter [duplicate]

This question already has an answer here:
How to filter my list of tuples of two input lists?
(1 answer)
Closed 3 months ago.
So i currently have this function for generating all possible combinations
func :: [a] -> [b] -> [(a, b)]
func xs ys = concat(map (\x -> map (\y -> (x,y))ys) xs)
Now I want to extend it so that i can filter it using an anonymous function as parameter.
Example:
func [1,2,3] [4,5] (\ a b -> a+b > 6) -> [(2,5),(3,4),(3,5)]
I tried to change the Typesignature of func:
from:
[a] -> [b] -> [(a, b)]
to:
[a] -> [b] -> (a1 -> a1 -> Bool) -> t
But it did not work.
Thank you in advance!
OP has stated that they changed their code to
func :: [a] -> [b] -> ((a,b) -> Bool) -> [(a, b)]
func xs ys f = filter f (tup)
where tup = concat(map (\x -> map (\y -> (x,y))ys) xs)
followed by what one assumes is
func [1,2,3] [4,5] (\a b -> a + b > 7)
which gives them the error
• Couldn't match expected type ‘Bool’ with actual type ‘(a, b) -> Bool’
• The lambda expression ‘\ a b -> a + b > 7’ has two value arguments, but its type ‘(a, b) -> Bool’ has only one
This is easy to fix; in fact the error message is relatively clear as GHC error messages go. (That's faint praise, admittedly.) Doing this instead should work, though I'm away from a Haskell compiler and can't run it:
func [1,2,3] [4,5] (\(a, b) -> a + b > 7)
Explanation: \a b -> a + b > 7 is an anonymous function taking two arguments, a and b, whereas \(a, b) -> a + b > 7 is an anonymous function taking a single argument which is a pair. The type of the last argument of func, ((a, b) -> Bool), is the type of the second anonymous function, not the first.
(This problem can also be fixed by making func's last argument be of type a -> b -> Bool, while passing the first anonymous function.)
I originally thought OP's tup was just zip, and recommended they use zip instead to improve legibility. OP correctly points out that their function has very little in common with zip except the type. There may still be a legibility win to be had, though; see this.

Isn't map takes a function and a list return a list?

map2_List :: (a -> b -> c) -> [a] -> [b] -> [c]
map2_List f [] _ = []
map2_List f (a:as) bs = map (f a) bs ++ map2_List f as bs
This is an example from my lecture, which try to apply a binary function to all pairs of elements of two lists. The part (f a) makes me confused. Does it suppose to be a value but not a function? Then what does map value bs do?
"The part (f a) makes me confused."
What is happening here is called currying and if you are coming to Haskell from imperative languages, it can be confusing.
In Haskell, all functions technically take a single value and return a single value. The returned value could be another function. Most programmers take the mental shortcut of thinking of functions taking all the values in their definition (the term is "saturated" BTW) and producing the final value, but even with this mental shortcut, there are a lot of times, like this, when it is not the case.
The function f is a binary function, and (f a) is that function partially applied. The type of (f a) :: b -> c.
"Then what does map value bs do?"
The map function (map :: (a->b) -> [a] ->[b]) is part of the standard prelude. It takes a simple function and applies it to each element in a list.
So let's take map (f a) bs apart:
map :: (b->c) -> [b] ->[c] applies a function to each element of a list
(f a) :: b -> c using currying a function f :: a -> b -> c is applied to an a and returns a function b -> c
bs is the second list from map2_List
The result of this function is [c], the function f applied to one element of the first list then applied to every element of the second list.

Function Composition Types

I struggle understanding function composition type results e.g
ghci> :t (id . const)
(id . const) :: a -> b -> a
ghci> :t ((:) . (+))
((:) . (+)) :: a -> [a -> a] -> [a -> a]
How do you guys generally derive function composition types?
That's simple, initially write down the types of both the functions:
> :t const
const :: a -> b -> a
> :t id
id :: a -> a
(id . const) gets translated to \x -> id (const x)
(const x) :: b -> a -- (Note that the type of `x` is `a` here)
id (const x) :: b -> a -- The output of id type will be the same as the input it takes
\x -> id (const x) :: a -> b -> a -- We aleady know the type of x
You can follow the same step for the next function.
Lets see that
> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
So in general:
Given two functions. Consider them as curried functions of one parameter, i.e. function of type
a -> b -> c -> ... -> z we'll consider as a function a -> ( b -> c -> ... -> z ) of one parameter that returns function with one parameter less.
Result type of second function should be same as parameter type of first. So consider parameter of first function as result parameter of second. Denote it as b.
Result type of whole composition should be equal a function from parameter type of second function (denote it as a) to result type of first function (denote it as c)
> :t (id . const)
> :t id
id :: a -> a
rename it to x -> x
> :t const
const :: a -> b -> a
Note that a here is not necessary the same that in previous type equation, some rename it to y -> z -> y
a = y
b = z -> y = x
c = x = z -> y
So result is a -> c = y -> ( z -> y) same as a -> b -> c
Overall semantics is equal to const
> :t ((:) . (+))
> :t (:)
(:) :: a -> [a] -> [a]
rename it to x -> [x] -> [x]
> :t (+)
(+) :: Num a => a -> a -> a
rename it to Num y => y -> y -> y
a = y
b = y -> y = x
c = [x] -> [x] = [y -> y] -> [y -> y]
Also we have restriction with type class Num y
So overall type well be Num y => a -> c = y -> [y -> y] -> [y -> y] same as Num a => a -> [a -> a] -> [a -> a]
Overall semantics is "make single parameter function that adds first parameter to numeric value and prepend that function to a given list of single parameter functions"
Exercises: Find the types of the function compositions below, by hand, not using GHCi. Do these exercises to build intuition for reasoning about function types. Remember that type a -> a, where a is a type variable, matches not only Int -> Int or [Bool] -> [Bool], but (a -> a) -> (a -> a) too. If you can't do the exercises, read below.
(*3) . (+2) (easy)
(*) . (+2)
(:) . (+2)
(:) . (*)
What is a function? It's something, that takes an input and returns an output. Not just random output, but output that depends on that input. For the same input the same function always returns the same output. A function transforms some stuff to another stuff. Input and output can be of different types. So imagine a function as a magical one-way tube: you put one thing in the hole labeled in and get something else from another hole named out, possibly of entirely different form. Like, you put a tomato in a tube and get a machine gun from the opposite end. But remember, functions are one-way, you can't put anything in the function's out hole.
a :: Int -> Int -- take a thing of type Int and return a thing of type Int
a n = n + 2
b :: Int -> Int
b n = n -- a function can even return the same value
c :: Int -> Bool -- an output can be of different type than input
c n = n `mod` 3 == 0
Haskell supports higher-order functions and is curried by default. This means that there are no multiple-argument functions in Haskell, every function accepts only 1 argument, but sometimes they can return functions. Haskell tubes always has one and only one hole in, but sometimes tubes pop tubes from the hole out! There are even tubes, in which you can put other tubes. Function types are right-associative, which means that a -> b -> c and a -> (b -> c) are the same thing.
:t (+) -- Num a => a -> a -> a
:t (:) -- a -> [a] -> [a]
So what's a function composition? It's when you compose functions into a new function, or when you fuse 2 tubes together, welding 1st's out to 2nd's in, so that whatever you put into the first tube's in hole, falls through and pops out of second tube's out hole.
A composed function then can be imagined as a long tube made out of two shorter tubes welded together. What would be the type of a composed function? Our new tube still has 2 holes in it, one labeled in, another labeled out. But remember, in of our new tube corresponded to in of a first part, and out of our new tube corresponded to out of the second part. So the type will be from whatever was input to 1st function to whatever was output of 2nd function.
:t (:[2,3]) . (+1) -- Num a => a -> [a]
-- why?
:t (+1) -- Num a => a -> a
:t (:[2,3]) -- Num a => a -> [a]
We joint 2 functions together, first's output to second's input, and get a new function that still has one input and one output. And that's exactly what function operator's type says:
:t (.) -- (b -> c) -> (a -> b) -> (a -> c)
Due to a historical accident, function composition goes from right to left, so (*3) . (+2) is a function that first adds 2 to a number, and then multiples by 3 the result. So how to deduce a type of a function composition? You joint the the input of 2nd function to an output of 1st function and throw away the types in between.
a -> b
b -> c becomes
a -> c
See also: [1], [2], [3], [4] for more ideas on how to use function composition and how to reason about function types.

about haskell higher order function

I'm asked to write a function "pipeline" which has type [a -> a] -> [a] -> [a], in such a pipeline, each function in the original function list is applied in turn to each element of the input. Eg, pipeline [(+1),(*2),pred] [1,2,3] would return [1,3,5].
The answer from the solution sheet is pipeline = map . foldr (.) id, which I don't quite understand. How can this solution come up?
One way to think about foldr is that
foldr f z xs
replaces every (:) in xs with f and the empty list with z
Note that
[(+1), (*2)]
is shorthand for
(+1) : (*2) : []
You should now be ables to see what
foldr (.) id ((+1) : (*2) : [])
evaluates to. And from this, you will be able to understand the whole expression.
Folds somehow are quite confusing, though they're actually very simple. Especially the right fold: it basically does nothing else but replace each : in a list with some alternative given function, and the list's nil with the init value. For your example:
foldr (.) id [(+1),(*2),pred]
≡ foldr (.) id ( (+1) : (*2) : pred : [] )
≡ (+1) . (*2) . pred . id
so this simply chains all the functions in a list to a big composition.
Once you've got this chain, applying it to all values in another list is trivial, an obvious job for map.
The crux of the problem is having a function of type : [a -> a] -> (a -> a) i.e convert a list of functions to a one function.
foldr has type: (b -> c -> c) -> c -> [b] -> c
Lets see how the type fits:
(b -> c -> c) -> c -> [b] -> c
Replace b with (a -> a) : ((a -> a) -> c -> c) -> c -> [a -> a] -> c
Replace c with (a -> a) : ((a -> a) -> (a -> a) -> (a -> a)) -> (a -> a) -> [a -> a] -> (a -> a)
Type of function (.) is (b -> c) -> (a -> b) -> a -> c, which is what we have in first type in the previous signature, (ours were all a), so we can use (.) there.
For second part i.e (a -> a) we can use id function.
Lets fit these: (.) id and we are left with [a -> a] -> (a -> a) part, which is what we needed
So, foldr (.) id gives us [a -> a] -> (a -> a)
After that the map part is just applying the resulting function of the foldr to each element of list.
NOTE: Use a whiteboard to understand all this till your subconscious get used to do this ;)

best way to write a-> ..->[a] recursive functions in haskell

So I keep having this small problem where I have something like
func :: a -> b -> [a] -- # or basically any a-> ...-> [a] where ... is any types ->
func x y = func' [x] y -- '# as long as they are used to generate a list of [a] from x
func' :: [a] -> b -> [a]
func' = undefined -- # situation dependant generates a list from
-- # each element and returns it as one long list
should I keep it like this?
should I use func' hidden by a where?
should I only use the [a] -> b -> [a] version and leave the responsibility of passing [variable] to the callee?
I might well need to compose these functions and might want to mess around with the order so I'm leaning towards option 3. What do you think?
It looks like you are trying to reinvent concatMap:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = concat . map f
So the "map" bit takes each element of the input list applies "f" to it. "f" takes a single "a" and returns a "[b]". These individual lists are then concatenated into a single list.
As Paul noted, func' can be replaced with concatMap and func.
And func itself reminds me of unfoldr from Data.List:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
It is used to generate a list of a from b.
By the way, func and func' are unfortunate names for such functions.

Resources