I'm reading the Reasoned Schemer.
I have some intuition about how conde works.
However, I can't find a formal definition of what conde/conda/condu/condi do.
I'm aware of https://www.cs.indiana.edu/~webyrd/ but that seems to have examples rather than definitions.
Is there a formal definition of conde, conda, condi, condu somewhere?
In Prolog's terms,
condA is "soft cut" a.k.a. *->, where A *-> B ; C is like (A, B ; not(A), C), only better ; whereas
condU is "committed choice", a combination of once and a soft cut so that (once(A) *-> B ; false) expresses (A, !, B) (with the cut inside):
condA: A *-> B ; C % soft cut,
% (A , B ; not(A) , C)
condU: once(A) *-> B ; C % committed choice,
% (A , !, B ; not(A) , C)
(with ; meaning "or" and , meaning "and", i.e. disjunction and conjunction of goals, respectively).
In condA, if the goal A succeeds, all the solutions are passed through to the first clause B and no alternative clauses C are tried.
In condU, once/1 allows its argument goal to succeed only once (keeps only one solution, if any).
condE is a simple disjunction of conjunctions, and condI is a disjunction which alternates between the solutions of its constituents, interleaving the streams thereof.
Here's an attempt at faithfully translating the book's code, w/out the logical variables and unification, into 18 lines of Haskell which is mostly a lazy Lisp with syntax.(*) See if this clarifies things:
Sequential stream combination ("mplus" of the book):
(1) [] ++: ys = ys
(2) (x:xs) ++: ys = x : (xs ++: ys)
Alternating stream combination ("mplusI"):
(3) [] ++/ ys = ys
(4) (x:xs) ++/ ys = x : (ys ++/ xs)
Sequential feed ("bind"):
(5) [] >>: g = []
(6) (x:xs) >>: g = g x ++: (xs >>: g)
Alternating feed ("bindI"):
(7) [] >>/ g = []
(8) (x:xs) >>/ g = g x ++/ (xs >>/ g)
"OR" goal combination ("condE"):
(9) (f ||: g) x = f x ++: g x
"Alternating OR" goal combination ("condI"):
(10) (f ||/ g) x = f x ++/ g x
"AND" goal combination ("all"):
(11) (f &&: g) x = f x >>: g
"Alternating AND" goal combination ("allI" of the book):
(12) (f &&/ g) x = f x >>/ g
Special goals true and false (or "success" and "failure"):
(13) true x = [x] -- a sigleton list with the same solution repackaged
(14) false x = [] -- an empty list, meaning the solution is rejected
And why are they called true and false? Because for any goal g we have, e.g.,
(g &&: true) x = g x >>: true = g x >>: (\ x -> [x] ) = g x
(false &&: g) x = false x >>: g = [] >>: g = [] = false x
-- ... etc.
Goals produce streams (possibly empty) of (possibly updated) solutions, given a (possibly partial) solution to a problem.
Re-write rules for all are:
(all) = true
(all g1) = g1
(all g1 g2 g3 ...) = (\x -> g1 x >>: (all g2 g3 ...))
= g1 &&: (g2 &&: (g3 &&: ... ))
(allI g1 g2 g3 ...) = (\x -> g1 x >>/ (allI g2 g3 ...))
= g1 &&/ (g2 &&/ (g3 &&/ ... ))
Re-write rules for condX are:
(condX) = false
(condX (else g1 g2 ...)) = (all g1 g2 ...) = g1 &&: (g2 &&: (...))
(condX (g1 g2 ...)) = (all g1 g2 ...) = g1 &&: (g2 &&: (...))
(condX (g1 g2 ...) (h1 h2 ...) ...) = (ifX g1 (all g2 ...)
(ifX h1 (all h2 ...) (...) ))
To arrive at the final condE and condI's translation, there's no need to implement the book's ifE and ifI, since they reduce further to simple operator combinations, with all the operators considered to be right-associative:
(condE (g1 g2 ...) (h1 h2 ...) ...) =
(g1 &&: g2 &&: ... ) ||: (h1 &&: h2 &&: ...) ||: ...
(condI (g1 g2 ...) (h1 h2 ...) ...) =
(g1 &&: g2 &&: ... ) ||/ (h1 &&: h2 &&: ...) ||/ ...
So there's no need for any special "syntax" in Haskell, plain binary infix operators suffice. Any combination can be used anywhere, with &&/ instead of &&: as needed. But on the other hand condI could also be implemented as a function to accept a collection (list, tree etc.) of goals to be fulfilled, that would use some smart strategy to pick of them one most likely or most needed etc, and not just simple binary alternation as in ||/ operator (or ifI of the book).
Next, the book's condA can be modeled by two new operators, ~~> and ||~, working together. We can use them in a natural way as in e.g.
g1 ~~> g2 &&: ... ||~ h1 ~~> h2 &&: ... ||~ ... ||~ gelse
which can intuitively be read as "IF g1 THEN g2 AND ... OR-ELSE IF h1 THEN ... OR-ELSE gelse":
"IF-THEN" goal combination is to produce a "try" goal which must be called with a failure-continuation goal:
(15) (g ~~> h) f x = case g x of [] -> f x ; ys -> ys >>: h
"OR-ELSE" goal combination of a try goal and a simple goal simply calls its try goal with a second, on-failure goal, so it's nothing more than a convenience syntax for automatic grouping of operands:
(16) (g ||~ f) x = g f x
With the "OR-ELSE" ||~ operator given less binding power than the "IF-THEN" ~~> operator and made right-associative too, and ~~> operator having still less binding power than &&: and the like, sensible grouping of the above example is automatically produced as
(g1 ~~> (g2 &&: ...)) ||~ ( (h1 ~~> (h2 &&: ...)) ||~ (... ||~ gelse ...) )
Last goal in an ||~ chain must thus be a simple goal. That's no limitation really, since last clause of condA form is equivalent anyway to simple "AND"-combination of its goals (or simple false can be used just as well).
That's all. We can even have more types of try goals, represented by different kinds of "IF" operators, if we want:
use alternating feed in a successful clause (to model what could've been called condAI, if there were one in the book):
(17) (g ~~>/ h) f x = case g x of [] -> f x ; ys -> ys >>/ h
use the successful solution stream only once to produce the cut effect, to model condU:
(18) (g ~~>! h) f x = case g x of [] -> f x ; (y:_) -> h y
So that, finally, the re-write rules for condA and condU of the book are simply:
(condA (g1 g2 ...) (h1 h2 ...) ...) =
g1 ~~> g2 &&: ... ||~ h1 ~~> h2 &&: ... ||~ ...
(condU (g1 g2 ...) (h1 h2 ...) ...) =
g1 ~~>! g2 &&: ... ||~ h1 ~~>! h2 &&: ... ||~ ...
(*) which is:
simple juxtaposition is curried function application,
f a b c =~= (((f a) b) c) =~= f(a, b, c)
(\ a -> b ) is lambda function, (lambda (a) b)
foo x = y is shortcut for foo = (\ x -> y )
a ## b = y is shortcut for (##) a b = y, definition of an infix operator ##
parentheses ( ) are just for grouping
[] is the empty list, and
: means cons -- both as a constructor ( lazy, as the whole language is lazy, i.e. call by need ), on the right of = in definitions; and as a destructuring pattern, on the left (or in pattern-matching case expressions).
The Reasoned Schemer covers conda (soft cut) and condu (committed choice). You'll also find explanations of their behavior in William Byrd's excellent dissertation on miniKanren. You've tagged this post as being about core.logic. To be clear core.logic is based on a more recent version of miniKanren than the one presented in The Reasoned Schemer. miniKanren is always interleaves disjunctive goals - condi and the interleaving variants no longer exist. conde is condi now.
By Example, using core.logic:
conde will run every group, succeed if at least one group succeeds, and return all results from all successful groups.
user> (run* [w q]
(conde [u#]
[(or* [(== w 1) (== w 2)])
(== q :first)]
[(== q :second)]))
([_0 :second] [1 :first] [2 :first])
conda and condu: both will stop after the first successful group(top to bottom)
conda returns all results from only the first successful group.
user> (run* [w q]
(conda [u#]
[(or* [(== w 1) (== w 2)])
(== q :first)]
[(== q :second)]))
([1 :first] [2 :first])
condu returns only one result from only the first successful group.
user> (run* [w q]
(condu [u#]
[(or* [(== w 1) (== w 2)])
(== q :first)]
[(== q :second)]))
([1 :first])
No idea what condi does though.
According to the ISO Prolog core standard control structures such as (,)/2, (;)/2 and (->)/2 are cut transparent. (*->)/2 is not found in the ISO Prolog core standard, but usually Prolog systems implement it also cut transparent.
This means one cannot translate:
once(A) *-> B;C
Into A, !, B; C. Because the later might be embedded in other control structures, and if there are disjunctions among them, these choice points would be also cut away. What on the other hand seems reasonable, to view it as A -> B; C,
known simply as ISO Prolog core standard if-then-else. The thus defined behaviour of the cut, is for example useful to break out of repeat loops, without throwing an exception. The usual programming pattern is more difficult to archive with if-then-else.
Related
So I have a list lets say [t, f, f, t, f] and an infix operator <==>. I want to connect the elements so the end result will look like this t <==> f <==> f <==> t <==> f . The problem that I am having though is that i get this result t <==> (f <==> (f <==> (t <==> f))). I know why this is happening because the resursion returns all previous elements but I have no idea how to fix it.
This is my code:
fun connect(l) =
case l of
x::[] => x
| x::xs => x1 <==> connect(xs);
the end result will look like this t <==> f <==> f <==> t <==> f.
problem is that i get this result t <==> (f <==> (f <==> (t <==> f)))
x <==> y <==> z could mean either (x <==> y) <==> z or x <==> (y <==> z).
If it is a problem that you fold to one side, try folding to the other:
fun connectLeft (x::xs) = foldl (op <==>) x xs
fun connectRight (x::xs) = foldr (op <==>) x xs
You can see how foldl and foldr are implemented.
Or you can see the illustration of folds as structural transformations (Wikipedia).
Edit: Assuming x <==> y <==> z means (x <==> y) && (y <==> z):
fun connect [] = ?
| connect [x] = ?
| connect [x,y] = x <==> y
| connect (x :: y :: rest) =
(x <==> y) && connect (y :: rest)
&& is not the name of SML's built-in logical and-operator, which is called andalso. If x <==> y returns a bool, then perhaps you mean to use andalso here.
Since connect supposedly returns either a bool or some user-defined truthy value, then maybe connect [x] = x, but maybe not.
In any case, connect [] should be considered: Is there a good default here, so that the function does not crash on empty lists?
If you want to write this using list combinators (like map, foldl, etc.) instead of manual recursion, what you can do is:
Convert the input list into a list of pairs, so [x, y, z] becomes [(x, y), (y, z)]. There is an SML function called zip, hiding away in the library ListPair... a few examples that might hint at how to do this:
ListPair.zip [x1,x2,x3] [y1,y2,y3] = [(x1,y1), (x2,y2), (x3,y3)]
ListPair.zip [x1,x2,x3] [x1,x2,x3] = [(x1,x1), (x2,x2), (x3,x3)]
ListPair.zip [x1,x2,x3] [y2,y3] = [(x1,y2), (x2,y3)]
Call map (op <==>) over the list of pairs.
Fold over the result with some && operator.
I am learning about monads from the book 'Learn You a Haskell for Great Good!' by Miran Lipovaca. I am trying to understand the associativity law for monads. Essentially, the law states that when you have a chain of monadic function applications with >>=, it shouldn't matter how they're nested.
The following code enables one to pass the result of a function of type a -> m b to a function of type b -> m c:
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = (\x -> g x >>= f)
However, for the example below:
ghci> let f x = [x, -x]
ghci> let g x = [x*3, x*2]
ghci> let h = f <=< g
ghci> h 3
[9, -9, 6, -6]
Are f x and g x both functions? It seems to be that they are lists with different values of x and not functions. How does the line let h = f <=< g work in the above code? f and g have to be functions since they are used with <=< but I am not sure what they are.
f x = [x, -x]
This is ordinary function definition syntax. We are defining a new function f, by writing down what it would produce when applied to a hypothetical value x.
let (whether as a statement or a let ... in ... expression) just introduces a block where you can make definitions, much like where. The definitions themselves use the same syntax as global ones do.
If you know how to define functions by writing e.g. plusOne n = n + 1 in a file, then this syntax is exactly the same (if you don't know how to do that, then I'd suggest reading through some introductory tutorials on fundamental Haskell syntax before you try to understand monadic function composition).
So after those definitions f and g are functions. f x and g x don't really make sense, since you don't have an x in scope to apply them to.
If you did have such a value in scope, then f x would be an expression that evaluates to a list, which involves calling the function f. It still wouldn't be true to say that f x or g x are functions.
So now it should be clear that let h = f <=< g is defining a new value h by applying the <=< operator to f and g.
Nothing's better for gaining a feeling of understanding, like working through the definitions by hand on a sheet of paper.
f x = [x, -x] can also be written f = (\ x -> [x, -x]). Thus
h 3
= {- by def of h -}
(f <=< g) 3
= {- by def of (<=<) -}
(\x -> g x >>= f ) 3
= {- by defs of f and g -}
(\x -> (\ x -> [x*3, x*2]) x >>= (\ x -> [x, -x])) 3
= {- by substitution -}
(\ x -> [x*3, x*2]) 3 >>= (\ x -> [x, -x])
= {- by substitution -}
[3*3,
3*2] >>= (\ x -> [x, -x])
= {- by definition of (>>=) for [] -}
concat [ (3*3) & (\ x -> [x, -x]) -- x & f == f x
, (3*2) & (\ x -> [x, -x])
]
= {- by definition of concat -}
(3*3) & (\ x -> [x, -x])
++ (3*2) & (\ x -> [x, -x])
=
[9, -9, 6, -6]
(edit) For a picture, and some more discussion of these Kleisli arrows and their composability, see this older answer of mine.
How can I properly prove that
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
sequenceA [] = pure []
sequenceA (x:xs) = pure (:) <*> x <*> sequenceA xs
is essentially the same to monad inputs as
sequenceA' :: Monad m => [m a] -> m [a]
sequenceA' [] = return []
sequenceA' (x:xs) = do
x' <- x
xs' <- sequenceA' xs
return (x':xs')
In spite of the constraint Applicative and Monad of course.
Here's a proof sketch:
Show that
do
x' <- x
xs' <- sequenceA' xs
return (x' : xs')
is equivalent to
do
f1 <- do
cons <- return (:)
x' <- x
return (cons x')
xs' <- sequenceA' xs
return (f1 xs')
This involves desugaring (and resugaring) do notation and applying the Monad laws.
Use the definition of ap:
ap m1 m2 = do { x1 <- m1; x2 <- m2; return (x1 x2) }
to turn the above code into
do
f1 <- return (:) `ap` x
xs' <- sequenceA' xs
return (f1 xs')
and then
return (:) `ap` x `ap` sequenceA' xs
Now you have
sequenceA' [] = return []
sequenceA' (x:xs) = return (:) `ap` x `ap` sequenceA' xs
Assume that pure and <*> are essentially the same as return and `ap`, respectively, and you're done.
This latter property is also stated in the Applicative documentation:
If f is also a Monad, it should satisfy
pure = return
(<*>) = ap
Since the Functor-Applicative-Monad proposal, implemented in GHC 7.10, Applicative is a superclass of Monad. So even though your two functions can't be strictly equivalent, since sequenceA's domain includes sequenceA''s domain, we can look at what happens in this common domain (the Monad typeclass).
This paper shows an interesting demonstration of desugaring do notation to applicative and functor operations (<$>, pure and <*>). If the expressions on the right hand side of your left-pointing arrows (<-) don't depend on each other, as is the case in your question, you can always use applicative operations, and therefore show that your hypothesis is correct (for the Monad domain).
Also have a look at the ApplicativeDo language extension proposal, which contains an example that's just like yours:
do
x <- a
y <- b
return (f x y)
which translates to:
(\x y -> f x y) <$> a <*> b
Substituting f for (:), we get:
do
x <- a
y <- b
return (x : y)
... which translates to...
(\x y -> x : y) <$> a <*> b
--And by eta reduction
(:) <$> a <*> b
--Which is equivalent to the code in your question (albeit more general):
pure (:) <*> a <*> b
Alternatively, you can make GHC's desugarer work for you by using the ApplicativeDo language extension and by following this answer to the SO question "haskell - Desugaring do-notation for Monads". I'll leave this exercise up to you (as it honestly goes beyond my capacities!).
My own two cents
There is no do notation for applicatives in Haskell. It can be seen specifically in this segment.
return and pure do exactly the same, but with different constraints, right?, so this part pure (:) and this part return (x:xs) are essentially the same.
Then, here x <- act you are getting the value of act, and then the value of the recursion xs <- seqn acts, to finally wrap it with the return.
And that's what pure (:) <*> x <*> sequenceA xs is essentially doing.
I know there are other posts about this, but mine is slightly different.
I have a function that performs the task of foldl, using foldr. I have the solution given to me, but would like help understanding.
foldlTest:: (b -> a -> b) -> [a] -> (b -> b)
foldlTest f xs = foldr (\x r b -> r (f b x))
(\b -> b)
xs
And It is called using something like this:
foldlTest (-) [1,2,3] 10 = -4
First thing I understand is that my function takes in 2 arguments, but 3 are given in the above test case. This means that the 10 will take part in a lambda expression I assume.
1) Does the 10 take the place of the b in b -> b? (then the b would be the initial accumulator value)
What I don't understand is what the (\x r b -> r (f b x)) part does.
2) What is the value of each of the variables? I am very confused about this lambda function.
3) What exactly does the lambda function do and how is it different from a regular foldr?
OK, since none of our resident Haskell experts has yet stepped up to explain this, I thought I'd have a go. Please everyone, feel free to correct anything you see wrong, because I'm really just feeling my way towards the answer here, and the following will by its very nature be a bit rambling.
First, as always in Haskell, it's a good idea to look at the types:
Prelude> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
Since we're just interested in lists here, and not generic Foldables, let's specialise this to:
foldl :: (b -> a -> b) -> b -> [a] -> b
and compare with the function you've been given:
foldlTest:: (b -> a -> b) -> [a] -> (b -> b)
Since Haskell functions are curried, which is another way of saying that -> arrows in type signatures are right associative, the last pair of parentheses there is unnecessary, so this is the same as:
foldlTest:: (b -> a -> b) -> [a] -> b -> b
Comparing with that for foldl above, we see that they're identical except for the fact that the last two parameters - the [a] and the b - have been flipped over.
So we can already observe that, while the library function foldl takes a fold function, a starting accumulator, and a list to fold, to produce a new accumulator, the foldlTest version takes a fold function, a list to fold, and a starting accumulator, to produce a new accumulator. That sounds like the exact same thing, which it is, but if we now reintroduce the pair of brackets which I took off a few steps ago, we see that foldlTest, in the form you've shown, can be thought of as:
taking a fold function and a list, and producing a function b -> b which describes how folding over the list transforms the initial accumulator into the final result.
Note in particular that what it returns, in this formulation, is indeed a function.
So now we're ready to look at the actual implementation that you've seen:
foldlTest f xs = foldr (\x r b -> r (f b x))
(\b -> b)
xs
I'll be the first to admit that this is rather complicated, even confusing. As ever, lets examine the types.
The input types are easy. We know from the above discussion that f has type b -> a -> b, and xs has type [a].
OK, what about that lambda? Let's take it in stages:
It takes 3 arguments, x, r and b, in that order.
The result of the function call is r (f b x). This already tells us a lot, if we sit down and think about it!
For one, we know f has type b -> a -> b, so from f b x we know that b has type b and x has type a.
As for r, we see that it must be a function, because it's applied to f b x. And we know the latter has type b (from the type signature of f). So r has type b -> c, for some type c.
Therefore our complicated lambda function has the type a -> (b -> c) -> b -> c, where c is some type we haven't yet determined.
Now comes a key point. This lambda is presented as the first argument (the fold function) to the foldr function. Therefore it must have the type d -> e -> e, for some types d and e.
Remember that functions are curried, and although it seems like the lambda's signature takes 3 arguments, we can reduce it to 2 by rewriting as a -> (b -> c) -> (b -> c). That's an exact match for the signature we know foldr is looking for, with d equal to a and e equal to b -> c.
And it we specialise foldr's signature so that it accepts this type of function, we find that it is:
foldr :: (a -> (b -> c) -> (b -> c)) -> (b -> c) -> [a] -> (b -> c)`
We still don't know what c is - but we don't have to wonder much longer. The above is the signature for a fold which goes over a list of as, and produces a function from b to c. The next argument to foldr, which is of type b -> c, is given (by the implementation we're trying to decipher) as \b -> b. This is just the identity function, of course, and crucially it is a function from a type to itself. So the b -> c type must actually be b -> b, or in other words c was the same as b all along!
So that lambda must have the following type:
a -> (b -> b) -> (b -> b)
It takes an a, and an endomorphism on b (that just means a function from b to itself), and returns another endomorphism on b. And this is the function we will fold the list of as with, taking the identity function as a starting point, to produce the endomorphism b -> b that will implement the left fold we're after.
The above type signature on its own doesn't give us much clue of how to implement it, given that a and b could be anything. However, we do have our function f that relates them - recall it takes a b and an a, and produces a b. Given that (by undoing the currying again), the above function requires us, given an a, a b -> b function, and a b, to produce another b, I can only see two non-trivial ways to do it:
apply the function to the b, then apply f to the a and the result.
apply f to the a and the b, then apply the b -> b function to the result
The second of these two is exactly what that lambda you are asking about does, as hopefully is now obvious from looking at it. (The first option would be written \x r b -> f (r b) x. I'm not actually sure what overall effect this would produce, although I haven't thought about it much.)
I've covered a lot of ground, although it feels like more than it really is, because I've tried to be very painstaking. To recap, what the foldlTest function does is, given a list of as and a function f :: b -> a -> b, produces a function b -> b that is built by starting with the identity function, and walking right-to-left along the list, changing the current function r :: b -> b to the one that sends b to r (f b x) - where x :: a is the element of the list we are currently at.
That's a rather algorithmic description of what foldlTest does. Let's try to see what it does to an actual list - not a concrete one, but let's say a 3-element list [a1, a2, a3]. We start with the identity function \b -> b, and successively transform it into:
b -> f b a3 (recall that r starts as the identity function)
b -> f (f b a2) a3 (this is just substituting the previous function as r into \b -> r (f b x), with a2 now playing the role of x)
b -> f (f (f b a1) a2) a3
I hope you can now see that this looks an awful lot like folding the list from the left with the same function f. And by "looks an awful lot like", I actually mean it's identical! (If you haven't seen or tried it before, try to write out the successive stages of foldl f b [a1, a2, a3] and you'll see the identical pattern.)
So apologies again that this has been a bit rambling, but I hope this has given you enough information to answer the questions you asked. And don't worry if it makes your brain hurt a bit - it does mine too! :)
The answer you've been given (not the one on SO, the one you cited in your question) seems to be more difficult than necessary. I assume it is intended to teach you some aspects of folds, but apparently this is not working well. I try to show what's happening and in the process answer your questions.
3) What exactly does the lambda function do and how is it different from a regular foldr?
The whole thing just builds a left fold (foldl) out of a right fold (foldr) and flips the last two arguments. It is equivalent to
foldTest f = flip (foldr (flip f))
foldTest f = flip (foldl f)
and it does so in a rather obscure way, by accumulating a function and does the flipping via a lambda.
1) Does the 10 take the place of the b in b -> b? (then the b would be the initial accumulator value) What I don't understand is what the (\x r b -> r (f b x)) part does.
Yes, correct. The 10 takes to role of the initial accumulator of a left fold.
2) What is the value of each of the variables? I am very confused about this lambda function.
To get an intuition as to what is happening, I find it helpful to do the actual lambda calculus step by step:
foldTest (-) [1,2,3] 10
foldTest (-) (1:(2:(3:[]))) 10
-- remember the definition of foldTest which is given in a point-free way
foldlTest f xs = foldr (\x r b -> r (f b x)) (\b -> b) xs
-- add the hidden parameter and you get
foldlTest f xs b' = (foldr (\x r b -> r (f b x)) (\b -> b) xs) b'
-- apply that definition with f = (-), xs = (1:(2:(3:[]))), b' = 10
(foldr (\x r b -> r ((-) b x)) (\b -> b) (1:(2:(3:[])))) 10
(foldr (\x r b -> r (b - x)) (\b -> b) (1:(2:(3:[])))) 10
-- the inner lambda function is curried, thus we can write it as
-- \x r (\b -> r (b - x)), which is equivalent but will be handy later on.
(
foldr (\x r -> (\b -> r (b - x))) (\b -> b) (1:(2:(3:[])))
) 10
-- keep in mind foldr is defined as
foldr f' b'' [] = b''
foldr f' b'' (x:xs') = f' x (foldr f' b'' xs')
-- apply second row of foldr with f' = (\x r -> (\b -> r (b - x))),
-- b'' = (\b -> b), x = 1 and xs' = (2:(3:[]))
(
(\x r -> (\b -> r (b - x))) 1 (foldr (\x r -> (\b -> r (b - x))) (\b -> b) (2:(3:[])))
) 10
-- apply accumulation lambda for the first time with x = 1,
-- r = foldr (\x r -> (\b -> r (b - x))) (\b -> b) (2:(3:[])) gives
(
\b -> (foldr (\x r -> (\b -> r (b - x))) (\b -> b) (2:(3:[]))) (b - 1)
) 10
-- now we repeat the process for the inner folds
(
\b -> (
foldr (\x r -> (\b -> r (b - x))) (\b -> b) (2:(3:[]))
) (b - 1)
) 10
(
\b -> (
(\x r -> (\b -> r (b - x))) 2 (foldr (\x r -> (\b -> r (b - x))) (\b -> b) (3:[]))
) (b - 1)
) 10
(
\b -> (
\b -> (foldr (\x r -> (\b -> r (b - x))) (\b -> b) (3:[])) (b - 2)
) (b - 1)
) 10
(
\b -> (
\b -> (
foldr (\x r -> (\b -> r (b - x))) (\b -> b) (3:[])
) (b - 2)
) (b - 1)
) 10
(
\b -> (
\b -> (
(\x r -> (\b -> r (b - x))) 3 (foldr (\x r -> (\b -> r (b - x))) (\b -> b) [])
) (b - 2)
) (b - 1)
) 10
(
\b -> (
\b -> (
\b -> (foldr (\x r -> (\b -> r (b - x))) (\b -> b) [])) (b - 3)
) (b - 2)
) (b - 1)
) 10
(
\b -> (
\b -> (
\b -> (
foldr (\x r -> (\b -> r (b - x))) (\b -> b) []
) (b - 3)
) (b - 2)
) (b - 1)
) 10
-- Now the first line of foldr's definition comes in to play
(
\b -> (
\b -> (
\b -> (
\b -> b
) (b - 3)
) (b - 2)
) (b - 1)
) 10
-- applying those lambdas gives us
(
\b -> (
\b -> (
\b -> (
\b -> b
) (b - 3)
) (b - 2)
) (b - 1)
) 10
-- So we can see that the foldTest function built another function
-- doing what we want:
(\b -> (\b -> (\b -> (\b -> b) (b - 3)) (b - 2)) (b - 1)) 10
(\b -> (\b -> (\b -> b) (b - 3)) (b - 2)) (10 - 1)
(\b -> (\b -> b) (b - 3)) ((10 - 1) - 2)
(\b -> b) (((10 - 1) - 2) - 3)
(((10 - 1) - 2) - 3)
((9 - 2) - 3)
(7 - 3)
4
By the definition of foldlTest, we have
foldlTest (-) xs b = foldr g n xs b
where
n b = b
g x r b = r (b - x)
By the definition of foldr, we have
foldr g n [x,y,z] = g x (foldr g n [y,z])
but also
foldr g n [x,y,z] b = g x (foldr g n [y,z]) b -- (1)
---- r -----------
= foldr g n [y,z] (b-x)
(when used "inside" the foldlTest), and so, by repeated application of (1),
= g y (foldr g n [z]) (b-x)
= foldr g n [z] ((b-x)-y)
= g z (foldr g n [] ) ((b-x)-y)
= foldr g n [] (((b-x)-y)-z)
= n (((b-x)-y)-z)
= (((b-x)-y)-z)
Thus an expression which is equivalent to the left fold is built by the right fold straight up, because g is tail recursive. And thus
foldlTest (-) [1,2,3] 10
-- [x,y,z] b
==
(((10 - 1) - 2) - 3))
==
foldl (-) 10 [1,2,3]
and so we see that no, the b in the n = (\b -> b) does not accept the 10, but rather it accepts the whole expression equivalent to the left fold that has been built by the right fold.
But yes, 10 is the initial accumulator value in the expression equivalent of the left fold, as intended, that has been built by the right fold.
Various instances of monads model different type of effects: for example, Maybe models partiality, List non-determinism, Reader read-only state. I would like to know if there is such an intuitive explanation for the monad instance of the stream data type (or infinite list or co-list), data Stream a = Cons a (Stream a) (see below its monad instance definition). I've stumbled upon the stream monad on a few different occasions and I would like to understand better its uses.
data Stream a = Cons a (Stream a)
instance Functor Stream where
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
instance Applicative Stream where
pure a = Cons a (pure a)
(Cons f fs) <*> (Cons a as) = Cons (f a) (fs <*> as)
instance Monad Stream where
xs >>= f = diag (fmap f xs)
diag :: Stream (Stream a) -> Stream a
diag (Cons xs xss) = Cons (hd xs) (diag (fmap tl xss))
where
hd (Cons a _ ) = a
tl (Cons _ as) = as
P.S.: I'm not sure if I'm very precise in my language (especially when using the word "effect"), so feel free to correct me.
The Stream monad is isomorphic to Reader Natural (Natural: natural numbers), meaning that there is a bijection between Stream and Reader Natural which preserves their monadic structure.
Intuitively
Both Stream a and Reader Natural a (Natural -> a) can be seen as representing infinite collections of a indexed by integers.
fStream = Cons a0 (Cons a1 (Cons a2 ...))
fReader = \i -> case i of
0 -> a0
1 -> a1
2 -> a2
...
Their Applicative and Monad instances both compose elements index-wise. It's easier to show the intuition for Applicative. Below, we show a stream A of a0, a1, ..., and B of b0, b1, ..., and their composition AB = liftA2 (+) A B, and an equivalent presentation of functions.
fStreamA = Cons a0 (Cons a1 ...)
fStreamB = Cons b0 (Cons b1 ...)
fStreamAB = Cons (a0+b0) (Cons (a1+b1) ...)
fStreamAB = liftA2 (+) fStreamA fStreamB
-- lambda case "\case" is sugar for "\x -> case x of"
fReaderA = \case 0 -> a0 ; 1 -> a1 ; ...
fReaderB = \case 0 -> b0 ; 1 -> b1 ; ...
fReaderC = \case 0 -> a0+b0 ; 1 -> a1+b1 ; ...
fReaderC = liftA2 (+) fReaderA fReaderB = \i -> fReaderA i + fReaderB i
Formally
The bijection:
import Numeric.Natural -- in the base library
-- It could also be Integer, there is a bijection Integer <-> Natural
type ReaderN a = Natural -> a
tailReader :: ReaderN a -> ReaderN a
tailReader r = \i -> r (i+1)
toStream :: ReaderN a -> Stream a
toStream r = Cons (r 0) (toStream (tailReader r))
fromStream :: Stream a -> ReaderN a
fromStream (Cons a s) = \i -> case i of
0 -> a
i -> fromStream s (i-1)
toStream and fromStream are bijections, meaning that they satisfy these equations:
toStream (fromStream s) = s :: Stream a
fromStream (toStream r) = r :: ReaderN a
"Isomorphism" is a general notion; two things being isomorphic usually means that there is a bijection satisfying certain equations, which depend on the structure or interface being considered. In this case, we are talking about the structure of monads, and we say that two monads are isomorphic if there is a bijection which satisfies these equations:
toStream (return a) = return a
toStream (u >>= k) = toStream u >>= (toStream . k)
The idea is that we get the same result whether we apply the functions return and (>>=) "before or after" the bijection. (The similar equations using fromStream can then be derived from these two equations and the other two above).
#Li-yao Xia's answer pretty much covers it, but if it helps your intuition, think of the Stream monad as modeling an infinite sequence of parallel computations. A Stream value itself is an (infinite) sequence of values, and I can use the Functor instance to apply the same function in parallel to all values in the sequence; the Applicative instance to apply a sequence of given functions to a sequence of values, pointwise with each function applied to the corresponding value; and the Monad instance to apply a computation to each value in the sequence with a result that can depend on both the value and its position within the sequence.
As an example of some typical operations, here are some sample sequences plus a Show-instance
instance (Show a) => Show (Stream a) where
show = show . take 10 . toList
nat = go 1 where go x = Cons x (go (x+1))
odds = go 1 where go x = Cons x (go (x+2))
giving:
> odds
[1,3,5,7,9,11,13,15,17,19]
> -- apply same function to all values
> let evens = fmap (1+) odds
> evens
[2,4,6,8,10,12,14,16,18,20]
> -- pointwise application of functions to values
> (+) <$> odds <*> evens
[3,7,11,15,19,23,27,31,35,39]
> -- computation that depends on value and structure (position)
> odds >>= \val -> fmap (\pos -> (pos,val)) nat
[(1,1),(2,3),(3,5),(4,7),(5,9),(6,11),(7,13),(8,15),(9,17),(10,19)]
>
The difference between the Applicative and Monadic computations here is similar to other monads: the applicative operations have a static structure, in the sense that each result in a <*> b depends only on the values of the corresponding elements in a and b independent of how they fit in to the larger structure (i.e., their positions in the sequence); in contrast, the monadic operations can have a structure that depends on the underlying values, so that in the expression as >>= f, for a given value a in as, the corresponding result can depend both on the specific value a and structurally on its position within the sequence (since this will determine which element of the sequence f a will provide the result).
It turns out that in this case the apparent additional generality of monadic computations doesn't translate into any actual additional generality, as you can see by the fact that the last example above is equivalent to the purely applicative operation:
(,) <$> nat <*> odds
More generally, given a monadic action f :: a -> Stream b, it will always be possible to write it as:
f a = Cons (f1 a) (Cons (f2 a) ...))
for appropriately defined f1 :: a -> b, f2 :: a -> b, etc., after which we'll be able to express the monadic action as an application action:
as >>= f = (Cons f1 (Cons f2 ...)) <*> as
Contrast this with what happens in the List monad: Given f :: a -> List b, if we could write:
f a = [f1 a, f2 a, ..., fn a]
(meaning in particular that the number of elements in the result would be determined by f alone, regardless of the value of a), then we'd have the same situation:
as >>= f = as <**> [f1,...,fn]
and every monadic list operation would be a fundamentally applicative operation.
So, the fact that not all finite lists are the same length makes the List monad more powerful than its applicative, but because all (infinite) sequences are the same length, the Stream monad adds nothing over the applicative instance.