I've been learning Haskell recently and came across something I don't quite understand: the parameters of a lambda function.
In the Learn You a Haskell for Great Good book, chap. 5, there are the following two functions:
elem' :: (Eq a) => a -> [a] -> Bool
elem' y ys = foldr (\x acc -> if x == y then True else acc) False ys
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
In the first function, the accumulator is listed as the lambda's second parameter, but then is the first to follow the lambda for foldl, which I took to mean it would be the first, not the second, thus, defying expectations.
Whereas, in the second function, it follows expectations, showing up as the lambda's first parameter, making the list that reverse' takes as a parameter the second for the lambda.
I tested both functions and they work as expected. I also noticed that one function involves a right fold and the other a left fold, but I'm not sure why that would alter the meaning of the parameters.
QUESTION: Can someone explain what I'm missing? Why are the parameters seeming to swap places?
foldl and foldr expect the accumulating function to have different formats. The two functions have the following types:
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
You're correct that in foldr, the accumulator is the second argument, and in foldl it's the left.
While this may seem unintuitive, it may help to think of foldl and foldr in terms of how they associate values in a list, the following images come from the "fold" page on the Haskell wiki:
Treating the natural order of the list as left to right: In foldr, the accumulator starts at the right hand side of the list, so it's natural that it's the second argument, while in foldl, the opposite is true.
It is just a convention that the accumulator in foldr is the second argument, and in foldl it is the first argument.
Why was this convention chosen?
The first reason was answered by #Joe. acc is the folded part of the list. In foldl it's left part but in foldr it's right part. So it's natural to provide acc as left operand (the first argument) to folding operator in foldl and as right operand (the second argument) to folding operator in foldr.
foldl should iterate over all the elements in the provided list, while foldr should not. You can provide folding operator to the foldr which can skip rest of elements in the list. The first example does that. The second argument acc in the foldr is thing which is not computed yet, it hold folding the rest of elements. And if you skip it in your folding operator it never be computed. In your example, if x == y you just "return" True (and skip rest elements), else you "return" acc which force to evaluate the next element in the list. So, foldr works lazyly, but foldl works strictly.
In Haskell is another convention. When operator can works lazyly then it usually have the first argument with strict semantic and the second with non strict. For example: &&, || are this sort of operators.
False && undefined => False
True || undefined => True
Folding operator in your the first example is lazy too.
(\x acc -> if x == y then True else acc) y undefined => True
And it can be rewrite in terms of || like this:
(\x acc -> x == y || acc)
Combining above reasons together we have what we have :-)
Related
I am struggling to think of a way to utilize these functions for this beginner level coding class that I am taking to learn functional programming in Haskell. The functions I have to write are shown below, asum is supposed to turn a list of integers [a1,a2,..,an] into the alternating sum a1-a2+a3-a4+.… and I am not sure how to approach it with these functions. The xor function is supposed to that computes the XOR of a list of Booleans. I need some help to understand how to use these functions and it would greatly appreciated. I am also new to Haskell so any explanations would help. Thanks I have to use map foldr foldl.
asum :: (Num a) => [a] -> a
xor :: [Bool] -> Bool
I would say start by running the following, one by one, in GHCi:
:t foldr
:info foldr
:doc foldr
:t foldl
:info foldl
:doc foldl
:t map
:info map
:doc map
Or better, open hoogle.haskell.org and search each of the above mentioned functions and click on the first link.
But I agree that Haskell documentation are difficult to read, especially for beginners. I'm a beginner and I have a lot of difficulty reading and understanding them.
Here's a function that uses map and foldr to show how foldr works:
printFoldr xs = foldr (\x acc -> "(" ++ x ++ " + " ++ acc ++ " )") "0" $ map show xs
Now running watch this:
printFoldr [1..5]
-- outputs the following:
"(1 + (2 + (3 + (4 + (5 + 0 ) ) ) ) )"
This shows us how foldr is evaluated. Before going into how foldr is evaluated, let's look briefly at map.
map show [1..5]
-- outputs the following:
["1","2","3","4","5"]
This means that map takes 2 arguments. A list and a function that is applied to each element of the list. The result is a new list with the function applied to each element. Thus, applying show to each number outputs their string representation.
Back to foldr. foldr takes 3 arguments:
a function of type a -> b -> b
an initial value of type b
a list of type [a]
foldr takes each and every value of the provided list and applies this function to it. What is special is that map retains the output of the function over each iteration and passes it to the function as its second argument on the next run. Therefore it is convenient to write the function that is passed foldr as follows: (\el acc -> do something). Now on the next iteration of foldr, acc will hold the value of the previous run and el will be the current element from the list. BTW, acc stands for accumulator and el for element. This enables us to reduce elements of the provided list to something completely new.
As you can see in printFoldr, the initial value is just an empty string but it gradually adds the lists elements to it showing how it would have reduced the elements of the list to their sum.
Here's an idea:
a1-a2+a3-a4+...
=
a1-(a2-(a3-(a4-(...(an-0)...))))
This fits pretty well to the foldr pattern of recursion,
foldr f z [a1,a2,a3,a4,...,an]
=
a1`f`(a2`f`(a3`f`(a4`f`(...(an`f`z)...))))
So it can be coded by setting f = ... and z = ... and calling
asum :: (Num a) => [a] -> a
asum xs = foldr f z xs
where
f = (...)
z = (...)
You will need to complete this definition.
For the XOR of a list of Booleans, assuming it is to be True if one and only one of them is True, and False otherwise, we can imagine this sequence of transformations:
[ True, False, False, True, True, False, ...]
==>
[ t, f, f, t, t, f, ...]
where t and f are some specially chosen numbers. And then we can find the sum of this second list (not alternating sum, just a sum of a list of numbers) and check whether it is equal to ... some (other?) special number, let's call it n1:
xor :: [Bool] -> Bool
xor bools = (aNumber ... n1)
where
list1 = bools
list2 = fun1 transform list1
transform False = f
transform True = t
f = ...
t = ...
aNumber = sum list2
n1 = ...
fun1 = ...
sum listOfNums = ...
fun1 is the function which transforms each element of its argument list according to the given function, called transform above. It is one of the two functions left from the three you were given, considering we've already been using foldr.
sum is to be implemented by using the last function that's left.
FYI,
map foo [a1,a2,a3,...,an]
=
[foo a1, foo a2, foo a3, ..., foo an]
and
foldl f z [a1,a2,a3,...,an]
=
((((z`f`a1)`f`a2)`f`a3)...)`f`an
I've been practicing with anonymous functions and got the following:
takeWhile' :: (a -> Bool) -> [a] -> [a]
takeWhile' f xs = foldl (\x y z -> if (f x) && z then x : y else y) xs [] True
which is basically a rewrite of the takeWhile function already in Haskell.
For those who don't know, the takeWhile function takes a list and a function and returns a new list with every element in the original list that satisfies the function until one of them gives false.
From my point of view everything seems to be correct, I have 3 arguments x y and z ready to use in my anonymous function, x being the list of numbers, y the empty list where I'll be inserting every element and z is basically a debouncer so that if one of the elements doesn't meet the requirements, we don't insert any more.
And yet Haskell gives me the following error:
"Occurs check: cannot construct the infinite type: a ~ Bool -> [a]"
Any idea why?
The fold function in fold takes as parameters the accumulator x, and the element y. So there is no z that is passed.
But even if that was somehow possible, there are still other issues. x is the accumulator here, so a list, that means that x : y makes no sense, since (:) :: a -> [a] -> [a] takes an element and a list, and constructs a new list.
You can however easily make use of foldr to implement a takeWhile function. Indeed:
takeWhile' p = foldr (\x -> if p x then (x :) else const []) []
We here thus check if the predicate holds, if that is the case, we preprend the accumulator with x. If not, we return [], regardless of the value of the accumulator.
Due to the laziness of foldr, it will not look for elements after an element has failed the accumulator, since const [] will ingore the value of the accumulator.
xs = [1,2,3]::[Float]
ys = map (+) xs
This was a question in an old test and there is no solution sheet.
The questions:
1) What kind of signature does ys have?
2) Explain why and draw how ys looks like
For the first question I know that xs is of type float and so should ys(I run the program in ghci too).
As for the second one I have no idea, because when I run the code nothing happens. When I run it and the run ys on a separate row I get an error.
Can someone help me with a hint?
For the first question I know that xs is of type float
er, no. xs has type [Float]: a list of floats.
and so should ys
ys does not have the same type as xs. You probably think so because you've read that + requires the arguments and result to have the same type:
(+) :: Num a => a -> a -> a
...or if you instantiate it to Float numbers
(+) :: Float -> Float -> Float
This is correct, nevertheless (+) is not an endomorphism (a function mapping a type to itself, as it would have to be if ys was the same type as xs) because it has two number arguments.
With map (+) you're considering (+) as a function of a single argument, not of two arguments. In most programming languages this would actually be an error, but not so in Haskell: in Haskell, all functions actually have only one argument. Functions with “multiple arguments” are really just functions on interesting types, that make it seem as if you're passing multiple arguments. In particular, the signature of (+) is actually shorthand for:
(+) :: Float -> (Float -> Float)
So, considered as a one-argument function, (+) actually maps numbers to number-endomorphisms. Hence,
map (+) :: [Float] -> [Float -> Float]
and
ys :: [Float -> Float]
– a list of number-functions. Specifically, it's this list:
ys = [(+) 1 , (+) 2 , (+) 3 ]
≡ [(1+) , (2+) , (3+) ]
≡ [\n -> 1+n, \n -> 2+n, \n -> 3+n]
I could, for example, use it like this:
GHCi> let [f,g,h] = ys in [f 3, g 2, h 1]
[4,4,4]
GHCi> map ($ 10) ys -- applies all functions separately to the number 10
[11,12,13]
GHCi> foldr ($) 0 ys -- applies all the functions one after another to 0
6
BTW, IMO you're asking the question the wrong way around. In Haskell, you don't want to consider some code and wonder what type it has – that is more an ML or even Lisp approach. I'd always start with the type signature, and work out the implementation “outside to in” (typed holes are very handy for this). This possibility is one of the big advantages of functional programming in comparison to procedural languages.
I don't have ghci at the moment, apologies if something I say is wrong.
xs is type [Float] and ys is of type [Float -> Float](it's a list of functions that each take a Float and return a Float). ys will be [(+) 1, (+) 2, (+) 3] because map applies (+) to each elements in xs. But you cannot print ys because functions do not derive Show
ys type is [Float -> Float], a list of functions that receive a number return the number +1 (first elem), the number + 2 (the second) and the number +3 (the last).
Please, bear in mind that + is a is applied with a single argument for each list element so it does return another function.
If you wanted to add all the items in the List, you should use a reduce function, such as foldl.
let zs = foldl (+) 0 xs
I hope this helps.
Cristóbal
When using foldr, the recursion occours inside the function, so,
when the given function doesn't strictly evaluate both sides, and
can return based on the first one, foldr must be a good solution,
because it will work on infinity lists
findInt :: Int -> [Int] -> Bool
findInt z [] = False
-- The recursion occours inside de given function
findInt z (x:xs)
| z == x = True
| otherwise = findInt z xs
equivalent to:
findInt' :: Int -> [Int] -> Bool
findInt' z = foldr (\x r -> if z == x then True else r) False
-- Where False is the "default value" (when it finds [], ex: findInt z [] = False)
A situation when foldr is not appropriate:
addAll :: Int -> [Int] -> Int
addAll z [] = z
-- The recursion occours outside the given function (+)
addAll z (x:xs) = addAll (z + x) xs
In this case, because + is strict (needs to evaluate both sides to return)
it would be greately useful if we applied it in some way which we could
have a redex (reducible expression), to make it possible to avoid thunks
and (when forced to run with previous evaluation, not lazy) in constant
space and without pushing to much onto the stack
(similar to the advantages of a for loop in imperative algorithms)
addAll' :: Int -> [Int] -> Int
addAll' z [] = z
addAll' z (x:xs) = let z' = z + x
in seq z' $ addAll' z' xs
equivalent to:
addAll'' :: Int -> [Int] -> Int
addAll'' z = foldl' (+) z
In this little case, using foldr (inside recursion) doesn't make sense
because it wouldn't make redexes.
It would be like this:
addAll''' :: Int -> [Int] -> Int
addAll''' z [] = z
addAll''' z (x:xs) = (+) x $ addAll''' z xs
The main objective of this question is first, know whether my premises are
right or where they could be better and second, help to make it more clear
for others who are also learning Haskell the differences between inside and
outside recursion, among the approaches, to have it clear in mind which one
could be more appropriated to a given situation
Helpful links:
Haskell Wiki
Stackoverflow - Implications of foldr vs. foldl (or foldl')
Aside from the fact that foldr is the natural catamorphism of a list, while foldl and foldl' are not, a few guidelines for their use:
you are correct on that foldr will always return, even on infinite lists, as long as the function is non-strict in its second argument, since the elements of the list are made available to the first argument of the function immediately (as opposed to foldl and foldl', where the elements of the list are not available to the first argument of the function until the list has been entirely consumed);
foldl' will be a better choice for non-infinite lists if you want to ensure constant space, since it's tail recursive, but it will always parse the entire list regardless of the strictness in the evaluation of the arguments to the function passed to it;
in general, foldr is equivalent to recursion, while foldl and foldl' are analogous to loops;
because of the fact that foldr is the natural catamorphism, if your function needs to recreate the list (for example, if your function is just the list constructor ':'), foldr would be more adequate;
with respect to foldl vs. foldl', foldl' is usually preferable because it will not build a huge thunk but, if the function passed to it is non strict in its first argument and the list is not infinite, foldl may return while foldl' may give an error (there is a good example in the Haskell wiki).
As a side note, I believe that you are using the term "inside recursion" to define foldr and "outside recursion" for foldl and foldl', but I haven't seen these terms before in the literature. More commonly these functions are just referred to as folding from the right and folding from the left respectively, terms that while may not be exactly correct, they give a good notion of the order in which the elements of the list are passed to the function.
I've a problem with implementing the last function using foldr1. I assume that it takes the right most element and treats it like the accumulator value, then applies binary function to the accumulator and its neighbor until it reaches the beginning of a given list. However, the code shown below doesn't work. Could someone tell me what's wrong with it? Instead of returning the last element it prints out the first one
last' list = foldr1 (\acc _ -> acc) list
foldr :: (a -> b -> b) -> b -> [a] -> b
As you can see, foldr makes the accumulator the second argument, while the value from the list is the first. foldr1 behaves the same way. So, as Lee said, you need to have your lamdba return the second argument rather than the first. Alternatively, you could say foldr1 (flip const)