Let's say I have two different functions that reverse a list:
revDumb : List a -> List a
revDumb [] = []
revDumb (x :: xs) = revDumb xs ++ [x]
revOnto : List a -> List a -> List a
revOnto acc [] = acc
revOnto acc (x :: xs) = revOnto (x :: acc) xs
revAcc : List a -> List a
revAcc = revOnto []
and now I want to prove that these functions indeed do the same thing. This is the proof that I came up with:
mutual
lemma1 : (acc, lst : List a) -> revOnto acc lst = revOnto [] lst ++ acc
lemma1 acc [] = Refl
lemma1 lst (y :: ys) = let rec1 = lemma1 (y :: lst) ys
rec2 = lemma2 y ys in
rewrite rec1 in
rewrite rec2 in
rewrite appendAssociative (revOnto [] ys) [y] lst in Refl
lemma2 : (x0 : a) -> (xs : List a) -> revOnto [x0] xs = revOnto [] xs ++ [x0]
lemma2 x0 [] = Refl
lemma2 x0 (x :: xs) = let rec1 = lemma2 x xs
rec2 = lemma1 [x, x0] xs in
rewrite rec1 in
rewrite sym $ appendAssociative (revOnto [] xs) [x] [x0] in rec2
revsEq : (xs : List a) -> revAcc xs = revDumb xs
revsEq [] = Refl
revsEq (x :: xs) = let rec = revsEq xs in
rewrite sym rec in lemma2 x xs
This even type checks and is total (despite the mutual recursion):
*Reverse> :total revsEq
Reverse.revsEq is Total
Note that lemma1 is effectively a stronger version of the lemma2, yet I seemingly need lemma2 since it simplifies the recursive case in lemma1.
The question is: can I do any better? Mutually recursive lemmas with lots of rewrites seem to be overly opaque.
If you do the recursion on a function that keeps revOnto's accumulator explicit, the proof can be quite short:
lemma1 : (acc, xs : List a) -> revOnto acc xs = revDumb xs ++ acc
lemma1 acc [] = Refl
lemma1 acc (y :: xs) =
rewrite lemma1 (y :: acc) xs in
appendAssociative (revDumb xs) [y] acc
revsEq : (xs : List a) -> revAcc xs = revDumb xs
revsEq [] = Refl
revsEq (x :: xs) = lemma1 [x] xs
Related
In my lecture, we had to define the function squareOn such that
with foldr.
The answer was
squareOn :: (Eq a, Num a) => [a] -> a -> a
squareOn = foldr (\x acc y -> if y == x then x*x else acc y) id
I undestand how foldr works, but I'm new at lambda expressions in Haskell. Is acc any type of function from Haskell? It would be nice if someone could explain how squareOn works. :)
This is a sort-of advanced usage of foldr. Normally, we see foldr used as in
fun xs = foldr (\x acc -> something using x and acc) base xs
or equivalently
fun = foldr (\x acc -> something using x and acc) base
which corresponds to the following recursive function:
fun [] = base
fun (x:xs) = something using x and acc
where acc = fun xs
Your case is a special case of this usage, where base, acc, and something using x and acc are functions. That is, we have
fun [] = \y -> base'
fun (x:xs) = \y -> something using x, acc, y
where acc = \y -> fun xs y
Moving back to foldr, we get
fun = foldr (\x acc -> \y -> something using x, acc, y) (\y -> base')
which can also be written as
fun = foldr (\x acc y -> something using x, acc, y) (\y -> base')
where a somehow confusing three-argument function appears to be passed to foldr.
Your specific case,
squareOn = foldr (\x acc y -> if y == x then x*x else acc y) id
corresponds to the explicit recursion:
squareOn [] = id
squareOn (x:xs) = \y -> if y == x then x*x else acc y
where acc = \y -> squareOn xs y
or
squareOn [] y = y
squareOn (x:xs) y = if y == x then x*x else squareOn xs y
which you should be able to understand.
Let's define this function without a lambda.
squareOn :: (Eq a, Num a) => [a] -> a -> a
squareOn = foldr f id
where
f x acc = g
where
g y | x == y = x * x
| otherwise = acc y
Now it's become what foldr usually looks like. It takes a function taking two arguments f and an initial value id.
When you pass [2, 4] to squareOn, it'll be expanded to foldr f id [2, 4], then f 2 (f 4 id) by the definition of foldr.
f 4 id returns a function that takes one argument y which returns 4 * 4 if y is 4, and returns id y otherwise. Let's call this function p.
p y | 4 == y = 4 * 4
| otherwise = id y
Now, f 2 (f 4 id) returns a function that takes one argument y which returns 2 * 2 if y is 2, and returns p y otherwise. When you name it q, it'll be like this.
q y | 2 == y = 2 * 2
| otherwise = p y
So squareOn [2, 4] 3, for example, is equivalent to q 3.
Whoever skipped those explicit arguments just made it unnecessarily harder on yourself to learn this stuff. It's totally superficial. Adding the explicit arguments, as specified by the type signature, gives us
squareOn :: (Eq a, Num a) => [a] -> a -> a
squareOn = foldr (\x acc y -> if y == x then x*x else acc y) id
squareOn xs = foldr (\x acc y -> if y == x then x*x else acc y) id xs
squareOn xs y = foldr (\x acc y -> if y == x then x*x else acc y) id xs y
squareOn xs y = foldr g id xs y where { g x acc y | y == x = x*x
| otherwise = acc y }
squareOn xs y = (case xs of {
[] -> id ;
(x:xs2) -> g x (foldr g id xs2)
}) y where { g x acc y | y == x = x*x
| otherwise = acc y }
Now we can see everything in play here, as opposed to having to keep it all in mind. There is playing chess, and then there's playing blindfold chess, and why play it blindfolded if you can just see?
So now it becomes obvious that passing that y around(*) from call to call unchanged actually has no purpose here, because it is the same y, and it is already in scope:
squareOn xs y = (case xs of {
[] -> y ;
(x:xs2) -> g x (foldr g y xs2)
}) where { g x acc | y == x = x*x
| otherwise = acc }
which simplifies back as just
squareOn xs y = foldr g y xs where { g x acc | y == x = x*x
| otherwise = acc }
{- cf.
squareOn xs y = foldr g id xs y where { g x acc y | y == x = x*x
| otherwise = acc y } -}
And to be pointlessly short and pointfree, like your original code,
squareOn = flip (foldr g) where { g x acc | y == x = x*x
| otherwise = acc }
Or it could be simplified to
squareOn xs y = case xs of {
[] -> y ;
(x:_) | y == x -> x*x ;
(_:xs2) -> squareOn xs2 y }
and further to a worker/wrapper with nested unary worker, whichever is clearer for you.
Passing the unchanged quantity around to have it in scope is only really needed in languages without nested scope, like Prolog.
(*)(so that explanation in full, which you asked for, about how this technique works, is actually in the linked answer).
I have the following code which gives out [2,4,6] :
j :: [Int]
j = ((\f x -> map x) (\y -> y + 3) (\z -> 2*z)) [1,2,3]
Why? It seems that just the "z-function" is being used, what happens to the "y-function"?
And how does map work in this particular case?
Let's compute:
((\f x -> map x) (\y -> y + 3) (\z -> 2*z)) [1,2,3]
^^^ f ^^^^^^^ ^^^ x ^^^^^
=
(map x) [1,2,3]
where f = \y -> y +3
x = \z -> 2*z
=
[x 1,x 2,x 3]
where f = \y -> y +3
x = \z -> 2*z
=
[2*1,2*2,2*3]
where f = \y -> y +3
x = \z -> 2*z
=
[2,4,6]
where f = \y -> y +3
x = \z -> 2*z
As we can see, f was taken as an argument, but was never used after that. Consequently \y -> y+3 never affected the final result.
The function map x is the function that applies function x to each element of a list. Note that, above, (map x) [1,2,3] is the same as map x [1,2,3]. Indeed, each function application g x1 x2 x3 x4 can be equivalently written as (((g x1) x2) x3) x4 by left-associating the applications.
Consider this trivial program:
module Study
g : Nat -> Nat -> Nat
g x y = x - y
f : Nat -> List Nat
f x = map (g x) [1, 2 .. x]
It gives an obvious error:
|
4 | g x y = x - y
| ^
When checking right hand side of g with expected type
Nat
When checking argument smaller to function Prelude.Nat.-:
Can't find a value of type
LTE y x
— Saying I should offer some proof that this subtraction is safe to perform.
Surely, in the given context, g is always invoked safely. This follows from the way enumerations behave. How can I extract a proof of that fact so that I can give it to the invocation of g?
I know that I can use isLTE to obtain the proof:
g : Nat -> Nat -> Nat
g x y = case y `isLTE` x of
(Yes prf) => x - y
(No contra) => ?s_2
This is actually the only way I know of, and it seems to me that in a situation such as we have here, where x ≥ y by construction, there should be a way to avoid a superfluous case statement. Is there?
For map (\y = x - y) [1, 2 .. x] there needs to be a proof \y => LTE y x for every element of [1, 2 .. x]. There is Data.List.Quantifiers.All for this: All (\y => LTE y x) [1, 2 .. x].
But constructing and applying this proof is not so straight-forward. You could either build a proof about the range function lteRange : (x : Nat) -> All (\y => LTE y x) (natRange x) or define a function that returns a range and its proof lteRange : (x : Nat) -> (xs : List Nat ** All (\y => LTE y x) xs). For simplicity, I'll show an example with the second type.
import Data.List.Quantifiers
(++) : All p xs -> All p ys -> All p (xs ++ ys)
(++) [] ys = ys
(++) (x :: xs) ys = x :: (xs ++ ys)
lteRange : (x : Nat) -> (xs : List Nat ** All (\y => LTE y x) xs)
lteRange Z = ([] ** [])
lteRange (S k) = let (xs ** ps) = lteRange k in
(xs ++ [S k] ** weakenRange ps ++ [lteRefl])
where
weakenRange : All (\y => LTE y x) xs -> All (\y => LTE y (S x)) xs
weakenRange [] = []
weakenRange (y :: z) = lteSuccRight y :: weakenRange z
Also, map only applies one argument, but (-) needs the proof, too. So with a little helper function …
all_map : (xs : List a) -> All p xs -> (f : (x : a) -> p x -> b) -> List b
all_map [] [] f = []
all_map (x :: xs) (p :: ps) f = f x p :: all_map xs ps f
We can roughly do what you wanted without checking for LTE during the run-time:
f : Nat -> List Nat
f x = let (xs ** prfs) = lteRange x in all_map xs prfs (\y, p => x - y)
I'm reading learnyouahaskell.com and currently investigating folds. In the book there are these examples:
maximum' :: (Ord a) => [a] -> a
maximum' = foldr1 (\x acc -> if x > acc then x else acc)
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
product' :: (Num a) => [a] -> a
product' = foldr1 (*)
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x acc -> if p x then x : acc else acc) []
head' :: [a] -> a
head' = foldr1 (\x _ -> x)
last' :: [a] -> a
last' = foldl1 (\_ x -> x)
I understand all of them except head' and tail'.
It is my understanding that the binary function should be applied to the accumulator and each element in the list in turn, and thus go through all the list. Why does this stop to the head (or tail, respectively)?
I understand _ (underscore) means "whatever" or "I don't care" but how does that stop going through all the list?
A foldr combines two items - the current "running total" sort of item, and the new item.
(\x _ -> x) takes the new item and discards it, retaining the original, so all of the remaining items are ignored.
Let's expand it:
foldr1 (\x _ -> x) [1..100000]
= (\x _ -> x) 1 (foldr (\x _ -> x) [2..100000])
= 1
Since the (foldr (\x _ -> x) [2..100000]) term isn't needed, it isn't evaluated (that's lazy evaluation in action, or rather inaction), so this runs fast.
With (\_ x -> x), the new item is taken and the old one is ignored - this keeps happening until the end of the list, so you get the last element. It doesn't avoid the other ones, it just forgets them all except the last.
A more human-readable name of (\_ x -> x) would refer to the fact that it ignores its first argument and returns its second one. Let's call it secondArg.
foldl1 (\_ x -> x) [1..4]
= let secondArg = (\_ x -> x) in foldl secondArg 1 [2..4]
= foldl (1 `secondArg` 2) [3..4]
= foldl ((1 `secondArg` 2) `secondArg` 3) [4]
= foldl (((1 `secondArg` 2) `secondArg` 3) `secondArg` 4) []
= (((1 `secondArg` 2) `secondArg` 3) `secondArg` 4)
= 4
Let's have a look at the definition of foldr1 first:
foldr1 :: (a -> a -> a) -> [a] -> a
foldr1 f [x] = x
foldr1 f (x : xs) = f x (foldr1 f xs)
Then, consider a call of your function head',
head' :: [a] -> a
head' = foldr1 (\x _ -> x)
to a list, say, [2, 3, 5]:
head' [2, 3, 5]
Now, filling in the right hand-side of head' gives
foldr1 (\x _ -> x) [2, 3, 5]
Recall that [2, 3, 5] is syntactic sugar for (2 : 3 : 5 : []). So, the second case of the definition of foldr1 applies and we yield
(\x _ -> x) 2 (foldr1 (\x _ -> x) (3 : 5 : [])
Now, reducing the applications results in 2 getting bound to x and foldr1 (\x _ -> x) (3 : 5 : []) getting bound to the ignored parameter _. What is left is the right-hand side of the lambda-abstraction with x replaced by 2:
2
Note that lazy evaluation makes that the ignored argument foldr1 (\x _ -> x) (3 : 5 : []) is left unevaluated and so—and this hopefully answers your question—the recursion stops before we have processed the remainder of the list.
I've looked at different folds and folding in general as well as a few others and they explain it fairly well.
I'm still having trouble on how a lambda would work in this case.
foldr (\y ys -> ys ++ [y]) [] [1,2,3]
Could someone go through that step-by-step and try to explain that to me?
And also how would foldl work?
foldr is an easy thing:
foldr :: (a->b->b) -> b -> [a] -> b
It takes a function which is somehow similar to (:),
(:) :: a -> [a] -> [a]
and a value which is similar to the empty list [],
[] :: [a]
and replaces each : and [] in some list.
It looks like this:
foldr f e (1:2:3:[]) = 1 `f` (2 `f` (3 `f` e))
You can imagine foldr as some state-machine-evaluator, too:
f is the transition,
f :: input -> state -> state
and e is the start state.
e :: state
foldr (foldRIGHT) runs the state-machine with the transition f and the start state e over the list of inputs, starting at the right end. Imagine f in infix notation as the pacman coming from-RIGHT.
foldl (foldLEFT) does the same from-LEFT, but the transition function, written in infix notation, takes its input argument from right. So the machine consumes the list starting at the left end. Pacman consumes the list from-LEFT with an open mouth to the right, because of the mouth (b->a->b) instead of (a->b->b).
foldl :: (b->a->b) -> b -> [a] -> b
To make this clear, imagine the function (-) as transition:
foldl (-) 100 [1] = 99 = ((100)-1)
foldl (-) 100 [1,2] = 97 = (( 99)-2) = (((100)-1)-2)
foldl (-) 100 [1,2,3] = 94 = (( 97)-3)
foldl (-) 100 [1,2,3,4] = 90 = (( 94)-4)
foldl (-) 100 [1,2,3,4,5] = 85 = (( 90)-5)
foldr (-) 100 [1] = -99 = (1-(100))
foldr (-) 100 [2,1] = 101 = (2-(-99)) = (2-(1-(100)))
foldr (-) 100 [3,2,1] = -98 = (3-(101))
foldr (-) 100 [4,3,2,1] = 102 = (4-(-98))
foldr (-) 100 [5,4,3,2,1] = -97 = (5-(102))
You probably want to use foldr in situations where the list can be infinite, and where the evaluation should be lazy:
foldr (either (\l ~(ls,rs)->(l:ls,rs))
(\r ~(ls,rs)->(ls,r:rs))
) ([],[]) :: [Either l r]->([l],[r])
And you probably want to use the strict version of foldl, which is foldl', when you consume the whole list to produce its output. It might perform better and might prevent you from having stack-overflow or out-of-memory exceptions (depending on compiler) due to extreme long lists in combination with lazy evaluation:
foldl' (+) 0 [1..100000000] = 5000000050000000
foldl (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci
foldr (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci
The first one –step by step– creates one entry of the list, evaluates it, and consumes it.
The second one creates a very long formula first, wasting memory with ((...((0+1)+2)+3)+...), and evaluates all of it afterwards.
The third one is like the second, but with the other formula.
Using
foldr f z [] = z
foldr f z (x:xs) = x `f` foldr f z xs
And
k y ys = ys ++ [y]
Let's unpack:
foldr k [] [1,2,3]
= k 1 (foldr k [] [2,3]
= k 1 (k 2 (foldr k [] [3]))
= k 1 (k 2 (k 3 (foldr k [] [])))
= (k 2 (k 3 (foldr k [] []))) ++ [1]
= ((k 3 (foldr k [] [])) ++ [2]) ++ [1]
= (((foldr k [] []) ++ [3]) ++ [2]) ++ [1]
= ((([]) ++ [3]) ++ [2]) ++ [1]
= (([3]) ++ [2]) ++ [1]
= ([3,2]) ++ [1]
= [3,2,1]
The definition of foldr is:
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
So here's a step by step reduction of your example:
foldr (\y ys -> ys ++ [y]) [] [1,2,3]
= (\y ys -> ys ++ [y]) 1 (foldr (\y ys -> ys ++ [y]) [] [2,3])
= (foldr (\y ys -> ys ++ [y]) [] [2,3]) ++ [1]
= (\y ys -> ys ++ [y]) 2 (foldr (\y ys -> ys ++ [y]) [] [3]) ++ [1]
= (foldr (\y ys -> ys ++ [y]) [] [3]) ++ [2] ++ [1]
= (\y ys -> ys ++ [y]) 3 (foldr (\y ys -> ys ++ [y]) [] []) ++ [2] ++ [1]
= (foldr (\y ys -> ys ++ [y]) [] []) ++ [3] ++ [2] ++ [1]
= [] ++ [3] ++ [2] ++ [1]
= [3,2,1]
Infix notation will probably be clearer here.
Let's start with the definition:
foldr f z [] = z
foldr f z (x:xs) = x `f` (foldr f z xs)
For the sake of brevity, let's write g instead of (\y ys -> ys ++ [y]). The following lines are equivalent:
foldr g [] [1,2,3]
1 `g` (foldr g [] [2,3])
1 `g` (2 `g` (foldr g [] [3]))
1 `g` (2 `g` (3 `g` (foldr g [] [])))
1 `g` (2 `g` (3 `g` []))
(2 `g` (3 `g` [])) ++ [1]
(3 `g` []) ++ [2] ++ [1]
[3] ++ [2] ++ [1]
[3,2,1]
My way of remembering this firstly, is through the use of an associative sensitive subtraction operation:
foldl (\a b -> a - b) 1 [2] = -1
foldr (\a b -> a - b) 1 [2] = 1
Then secondly , foldl starts at the leftmost or first element of the list whereas foldr starts at the rightmost or last element of the list. It is not obvious above since the list has only one element.
My mnemonic is this: The left or right describes two things:
the placement of the minus (-) symbol
the starting element of the list
I tend to remember things with movement, so I imagine and visualize values flying around. This is my internal representation of foldl and foldr.
The diagram below does a few things:
names the arguments of the fold functions in a way that is intuitive (for me),
shows which end each particular fold works from, (foldl from the left, foldr from the right),
color codes the accumulator and current values,
traces the values through the lambda function, mapping them onto the next iteration of the fold.
Mnemonically, I remember the arguments of foldl as being in alphabetical order (\a c ->), and the arguments of foldr to be in reverse alphabetical order (\c a ->). The l means take from the left, the r means take from the right.