Haskell `let` bindings in lambda calculus - haskell

I want to understand how let bindings work in Haskell (or maybe lambda calculus, if the Haskell implementation differs?)
I understand from reading Write you a Haskell that this is valid for a single let binding.
let x = y in e == (\x -> e) y
This makes sense to me, since it's consistent with how bindings work in the lambda calculus. Where I'm confused is using multiple let bindings, where one binding can reference the bindings above. I will provide a trivial example.
Original code:
let times x y = x * y
square x = times x x
in square 5
My guess at the implementation:
(\square times -> square 5) (\x -> times x x) (\x -> x * x)
This seems not to work because times is not defined when square is called by the lambda. However, this can by solved by this implementation:
(\square -> square 5) ((\times x -> times x x) (\x -> x * x))
Is this the proper way to implement this binding, at least in the lambda calculus?

The times/square example can be expressed in terms of lambda functions using scoping:
(\times -> (\square -> square 5)(\x -> times x x))(\x y -> x * y)
But scoping isn't enough for recursive or mutually recursive let-bindings like
let ones = 1 : ones in take 5 ones
let even n = n == 0 || odd (abs n - 1)
odd n = n /= 0 && even (abs n - 1)
in even 7
In the lambda calculus you can define the y-combinator for recursion as
(\f -> (\x -> f (x x))(\x -> f (x x)))
This lets you define functions and values in terms of themselves. That formulation isn't legal haskell due to typing constraints but there are ways around that.
Using the y-combinator lets us express the above let-bindings using the lambda calculus:
(\ones -> take 5 ones)((\f -> (\x -> f (x x))(\x -> f (x x)))(\ones -> 1 : ones))
(\evenodd -> evenodd (\x y -> x) 7)((\f -> (\x -> f (x x))(\x -> f (x x)))(\evenodd c -> c (\n -> n == 0 || evenodd (\x y -> y) (abs n - 1)) (\n -> n /= 0 && evenodd (\x y -> x) (abs n - 1))))

Note that multiple let bindings can be reduced to a single one, defining a pair (tuple, in the general case). E.g. we can rewrite
let times x y = x * y
square x = times x x
in square 5
as
let times = \x y -> x * y
square = \x -> times x x
in square 5
then
let (times, square) = (\x y -> x * y, \x -> times x x)
in square 5
then, if wanted,
let pair = (\x y -> x * y, \x -> fst pair x x)
in snd pair 5
After that, we can apply the usual lambda calculus translation. If the pair definition ends up to be recursive, as in the case above, we need a fixed point combinator.
(\pair -> snd pair 5) (fix (\pair -> (\x y -> x * y, \x -> fst pair x x)))
Note that this translation does not play along type inference algorithms, which handle let in a special way, introducing polymorphism. This is not important if we only care about the dynamic aspects of our program, though.

I will answer my own question to maybe provide a helpful perspective to those who visit this question.
We want to implement the following program with two let bindings:
let times a b = a * b
square x = times x x
in square 5
To start with, let's simplify this to the essence of what we want:
square 5
Simple enough. However, square in this case is undefined! Well, we can bind it using the mechanism our language provides us with - a lambda. This gives us (\ square -> square 5) (\x -> times x x). Now square is defined, but its cousin times is not... Well, we need another lambda! Our final program should look like this:
(\times -> (\square -> square 5) (\x -> times x x)) (\a b -> a * b)
Notice that the (\times -> ...) completely encloses our last step, so that times will be in scope as it is bound. This is consistent with the answer given by #rampion, and reduces as follows:
(\times -> (\square -> square 5) (\x -> times x x)) (\a b -> a * b) =>
(\square -> square 5) (\x -> (\a b -> a * b) x x) =>
(\square -> square 5) (\x -> (\b -> x * b) x) =>
(\square -> square 5) (\x -> x * x) =>
(\x -> x * x) 5 =>
5 * 5 =>
25
If the square function had not depended on times, we could have easily written (\times square -> ..... The dependency means that we must nest these two environments, one containing times, and another inside of that which can use its definition.
Thanks for all of your help! I'm blown away by the simplicity and power of the lambda calculus.

Related

How to understand nested lambda functions in Haskell

I am trying to understand the meaning of the following 2 lambda expressions in Haskell:
f = \x -> x (\y -> x y)
g = \x -> (\y -> y) x
I tried to convert them, and I got this:
f x y = x x y
g x y = y x
Is this correct? I assumed the arguments of both functions have to be x and y, as they are both found in a lambda expression in the function description. I basically understood it this way: f(x) = x f(y) and f(y) = y x. And for g, g(x) = g(y) x and g(y) = y. But as I am new to Haskell, I'm not very confident with these types of conversion. If not correct, what would be a correct conversion?
Neither is correct. Your solution uses the functions
f x y = x x y
g x y = y x
which actually mean
f = \x -> (\y -> x x y)
g = \x -> (\y -> y x)
and those differ from the original expressions
f = \x -> x (\y -> x y)
g = \x -> (\y -> y) x
The above two equations can be rewritten as
f x = x (\y -> x y)
g x = (\y -> y) x
But from here, there is no way to turn the remaining lambdas into more arguments for f or g. At best, we can simplify them using beta/eta conversion and get
f x = x x -- eta (\y -> x y) = x
g x = x -- beta (\y -> y) x = x
(Also see the comment below by Will Ness, who points out that through an additional eta expansion in f we could reach the OP's definition. Still, that is incidental.)
Finally, note that Haskell will not accept f x = x x since that can not be typed, unless we use rank-2 types and explicitly provide a type annotation like f :: (forall a. a) -> b. The original code f = \x -> x (\y -> x y) suffers from the same issue. That would also be fine in untyped languages, e.g. the untyped lambda calculus in programming languages theory.
The :type command at the GHCi prompt is your friend. Let's take your second example first
λ> :type let g = \x -> (\y -> y) x in g
let g = \x -> (\y -> y) x in g :: p -> p
So g is well-typed and is a convoluted way to write an identity function :: p -> p. Specifically, g takes some x and applies an identity function (\y -> y) to x, resulting in x. GHCi in giving the type uses a fresh type name p, to avoid confusion. No your g x y = ... is not equivalent. (Check it with :type.)
You can abbreviate :type to just :t. Then let's take your first example.
λ> :t let f = \x -> x (\y -> x y) in f
* Occurs check: cannot construct the infinite type: t2 ~ t2 -> t3
* In the first argument of `x', namely `(\ y -> x y)'
In the expression: x (\ y -> x y)
In the expression: \ x -> x (\ y -> x y)
* Relevant bindings include
x :: t2 -> t3 (bound at <interactive>:1:10)
f :: (t2 -> t3) -> t3 (bound at <interactive>:1:5)
Errk. Is your suggested f the same as that?
λ> :t let f x y = x x y in f
* Occurs check: cannot construct the infinite type:
t3 ~ t3 -> t4 -> t5
* In the first argument of `x', namely `x'
It at least looks like a similar error message. What are these t2, t3, t4, t5? Again it's GHCi using fresh names for the types, to avoid confusion.
Looking at the let f = ..., GHCi sees x is applied to something, so it gives x :: t2 -> t3 where t2 is the type of its argument, t3 is the return type. It also sees f = \x -> x (blah). So the return type of f must be whatever x returns, i.e. t3, and the argument to f is x. So f :: (t2 -> t3) -> t3.
Inside the (blah), there's x applied to something. So the something (i.e. y) must be the type of x's argument, and the return type must be x's return type. I.e. (\y -> x y) :: t2 -> t3. Errk: then we must have x's argument type same as that, because x is applied to it. And the way we write 'same as' is with ~.
Then the error message tells you GHCi is trying to make sense of t2 ~ (t2 -> t3). (-> binds tighter than ~.) And if you try to subsitute that equivalence for t2 into the RHS you'll get t2 ~ (((... -> t3) -> t3)-> t3) ad infinitum.
Your suggested equivalent for f x y = is not equivalent (the message/typing is a little different). But they're both infinite types, so not allowed.

Meaning of different lambda functions and characters

At the moment I am learning Haskell, but I am struggling with the syntax of a few example. What do they exactly mean?
First: What is the difference between these two lambdas (-> \y and y)?
lambda1 = \x -> \y -> x + y
lambda2 = \x y -> x + y
Second: What does this mean? Is this a lambda that act as a "pseudo" list generator that generates a list with 3 elements. How can I create such a list?
lambda3 = [\x -> x+1, \x -> 2*x, \x -> x^2]
Third: What does the \_ exactly mean?
lambda4 = \_ -> (\x -> x+1, \() -> 'a')
lambda2 is syntactic sugar for lambda1. All of these are equivalent:
f = \x -> \y -> x + y
f = \x y -> x + y
f x = \y -> x + y
f x y = x + y
f x y = (+) x y
f x = (+) x
f = (+)
lambda3 is a list of unary functions on numbers. Each function has the type (Num a) => a -> a, so the list has type (Num a) => [a -> a]. You could produce a list of values from this with map or a list comprehension:
fs = [\x -> x+1, \x -> 2*x, \x -> x^2]
map (\f -> f 3) fs
map ($ 3) fs
[f 3 | f <- fs]
==
[4, 6, 9]
lambda4 uses pattern-matching syntax. For example, if you have a data type:
data Foo = Foo Int String
Then you can write a lambda that pattern-matches on it:
f = \ (Foo n s) -> concat (replicate n s)
f (Foo 3 "bar") == "barbarbar"
(But unlike case, there is no way to provide alternative patterns if Foo has multiple constructors.)
The _ pattern just says “accept a value and ignore it”, so lambda4 is a function that accepts an argument, ignores it, and returns a pair (2-tuple) of unary functions, the first of type (Num a) => a -> a and the second of type () -> Char, so its type is Num a => r -> (a -> a, () -> Char).
lambda4 = \_ -> (\x -> x+1, \() -> 'a')
lambda4 = \ignored -> (\x -> x+1, \() -> 'a')
(inc, getA) = lambda4 ()
inc 3 == 4
getA () == 'a'
Functions that ignore their arguments can be constructed with the const function, and operator sections ((+ 1)) are typically preferred over lambdas (\x -> x + 1), so you can also write the above as:
lambda4 = const ((+ 1), const 'a')
On your second question, lambda3 is just a bad variable name. this is a list of functions of type Num a => a -> a. You can verify that by typing the following in ghci:
:t [\x -> x+1, \x -> 2*x, \x -> x^2]
First: What is the difference between these two lambdas (-> \y and y)?
There is no difference. Both produce the same output for the same input, and since they're pure functions, you can be sure that they produce no external effects that you wouldn't see.
The difference lies in that the first lambda uses syntactic sugar for currying.
\x y -> x + y is equal to \x -> \y -> x + y. Now, don't you think it looks a lot like type signatures, such as foo :: Int -> Int -> Int ? ;)
It means that the function foo takes 2 Int and produces an Int.
Since I don't have a very precise answer for the 2nd…
Third: What does the \_ exactly mean?
It's a lambda function (\) to which is associated the _ variable. _ is used as a placeholder to say “I don't care about the content of this variable, I'm even going to give it a proper name”.
There is no -> y. The correct way to read this is
(\ x -> (\ y -> (x + y)))
As it happens, Haskell has "curried functions", which means that
\ x y -> (x + y)
just happens to be equivalent to the above.
lambda3 is a list which contains three elements. Each of those elements happens to be a function. Functions are data in Haskell; you can pass them as arguments, return them as results, stuff them into lists, etc.
lambda3 = [ (\x -> x+1) , (\x -> 2*x) , (\x -> x^2) ]
lambda4 = \_ -> (\x -> x+1, \() -> 'a')
The "_" character basically means "I don't care what this is; ignore it". You can use it anywhere you can use a pattern. For example,
foobar x _ z = x + y
is a 3-argument function that completely ignores argument #2. Read about pattern matching and this should become clear. (I.e., it's not to do with lambdas, it's to do with patterns.)

Why is the type of this function (a -> a) -> a?

Why is the type of this function (a -> a) -> a?
Prelude> let y f = f (y f)
Prelude> :t y
y :: (t -> t) -> t
Shouldn't it be an infinite/recursive type?
I was going to try and put into words what I think it's type should be, but I just can't do it for some reason.
y :: (t -> t) -> ?WTFIsGoingOnOnTheRHS?
I don't get how f (y f) resolves to a value. The following makes a little more sense to me:
Prelude> let y f x = f (y f) x
Prelude> :t y
y :: ((a -> b) -> a -> b) -> a -> b
But it's still ridiculously confusing. What's going on?
Well, y has to be of type (a -> b) -> c, for some a, b and c we don't know yet; after all, it takes a function, f, and applies it to an argument, so it must be a function taking a function.
Since y f = f x (again, for some x), we know that the return type of y must be the return type of f itself. So, we can refine the type of y a bit: it must be (a -> b) -> b for some a and b we don't know yet.
To figure out what a is, we just have to look at the type of the value passed to f. It's y f, which is the expression we're trying to figure out the type of right now. We're saying that the type of y is (a -> b) -> b (for some a, b, etc.), so we can say that this application of y f must be of type b itself.
So, the type of the argument to f is b. Put it all back together, and we get (b -> b) -> b — which is, of course, the same thing as (a -> a) -> a.
Here's a more intuitive, but less precise view of things: we're saying that y f = f (y f), which we can expand to the equivalent y f = f (f (y f)), y f = f (f (f (y f))), and so on. So, we know that we can always apply another f around the whole thing, and since the "whole thing" in question is the result of applying f to an argument, f has to have the type a -> a; and since we just concluded that the whole thing is the result of applying f to an argument, the return type of y must be that of f itself — coming together, again, as (a -> a) -> a.
Just two points to add to other people's answers.
The function you're defining is usually called fix, and it is a fixed-point combinator: a function that computes the fixed point of another function. In mathematics, the fixed point of a function f is an argument x such that f x = x. This already allows you to infer that the type of fix has to be (a -> a) -> a; "function that takes a function from a to a, and returns an a."
You've called your function y, which seems to be after the Y combinator, but this is an inaccurate name: the Y combinator is one specific fixed point combinator, but not the same as the one you've defined here.
I don't get how f (y f) resolves to a value.
Well, the trick is that Haskell is a non-strict (a.k.a. "lazy") language. The calculation of f (y f) can terminate if f doesn't need to evaluate its y f argument in all cases. So, if you're defining factorial (as John L illustrates), fac (y fac) 1 evaluates to 1 without evaluating y fac.
Strict languages can't do this, so in those languages you cannot define a fixed-point combinator in this way. In those languages, the textbook fixed-point combinator is the Y combinator proper.
#ehird's done a good job of explaining the type, so I'd like to show how it can resolve to a value with some examples.
f1 :: Int -> Int
f1 _ = 5
-- expansion of y applied to f1
y f1
f1 (y f1) -- definition of y
5 -- definition of f1 (the argument is ignored)
-- here's an example that uses the argument, a factorial function
fac :: (Int -> Int) -> (Int -> Int)
fac next 1 = 1
fac next n = n * next (n-1)
y fac :: Int -> Int
fac (y fac) -- def. of y
-- at this point, further evaluation requires the next argument
-- so let's try 3
fac (y fac) 3 :: Int
3 * (y fac) 2 -- def. of fac
3 * (fac (y fac) 2) -- def. of y
3 * (2 * (y fac) 1) -- def. of fac
3 * (2 * (fac (y fac) 1) -- def. of y
3 * (2 * 1) -- def. of fac
You can follow the same steps with any function you like to see what will happen. Both of these examples converge to values, but that doesn't always happen.
Let me tell about a combinator. It's called the "fixpoint combinator" and it has the following property:
The Property: the "fixpoint combinator" takes a function f :: (a -> a) and discovers a "fixed point" x :: a of that function such that f x == x. Some implementations of the fixpoint combinator might be better or worse at "discovering", but assuming it terminates, it will produce a fixed point of the input function. Any function that satisfies The Property can be called a "fixpoint combinator".
Call this "fixpoint combinator" y. Based on what we just said, the following are true:
-- as we said, y's input is f :: a -> a, and its output is x :: a, therefore
y :: (a -> a) -> a
-- let x be the fixed point discovered by applying f to y
y f == x -- because y discovers x, a fixed point of f, per The Property
f x == x -- the behavior of a fixed point, per The Property
-- now, per substitution of "x" with "f x" in "y f == x"
y f == f x
-- again, per substitution of "x" with "y f" in the previous line
y f == f (y f)
So there you go. You have defined y in terms of the essential property of the fixpoint combinator:
y f == f (y f). Instead of assuming that y f discovers x, you can assume that x represents a divergent computation, and still come to the same conclusion (iinm).
Since your function satisfies The Property, we can conclude that it is a fixpoint combinator, and that the other properties we have stated, including the type, are applicable to your function.
This isn't exactly a solid proof, but I hope it provides additional insight.

Y combinator, Infinite types and Anonymous recursion in Haskell

I was trying to solve the maximal subsequence sum problem and came up with a neato solution
msss :: (Ord a, Num a) => [a] -> a
msss = f 0 0
f gmax _ [] = gmax
f gmax lmax (x:xs) =
let g = max (lmax + x)
in f (g gmax) (g 0) xs
You call the wrapper function msss, which then calls f, which in turn actually does the work.
The solution is good and afaik working correctly. If for some reason I had to solve the maximal subsequence sum problem in production code, that is how I would do it.
However that wrapper function really bugs me. I love it how in haskell, if you are persistent enough you can write your entire program on a single line, to truly drive home the point that a program is pretty much just one big expression. So I figured I'd try and eliminate the wrapper function for the extra challenge.
It's now I run into the classic problem: How to do anonymous recursion? How do you do recursion when you can't give names to functions? Thankfully the fathers of computing solved this problem ages ago by discovering Fixed-Point Combinators, with the most popular being the Y Combinator.
I've made various attempts to get a Y combinator set up, but they can't get past the compiler.
msss' :: [Int] -> Int
msss' = (\y f x -> f (y y f) x)
(\y f x -> f (y y f) x)
(\g' gmax lmax list -> if list == []
then gmax
else g' (max gmax lmax + head list)
(max 0 lmax + head list)
tail list)
just gives
Prelude> :l C:\maxsubseq.hs
[1 of 1] Compiling Main ( C:\maxsubseq.hs, interpreted )
C:\maxsubseq.hs:10:29:
Occurs check: cannot construct the infinite type:
t0 = t0 -> (([Int] -> Int) -> [Int] -> Int) -> [Int] -> Int
In the first argument of `y', namely `y'
In the first argument of `f', namely `(y y f)'
In the expression: f (y y f) x
C:\maxsubseq.hs:11:29:
Occurs check: cannot construct the infinite type:
t0 = t0 -> (([Int] -> Int) -> [Int] -> Int) -> [Int] -> Int
In the first argument of `y', namely `y'
In the first argument of `f', namely `(y y f)'
In the expression: f (y y f) x
C:\maxsubseq.hs:12:14:
The lambda expression `\ g' gmax lmax list -> ...'
has four arguments,
but its type `([Int] -> Int) -> [Int] -> Int' has only two
In the second argument of `\ y f x -> f (y y f) x', namely
`(\ g' gmax lmax list
-> if list == [] then
gmax
else
g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)'
In the expression:
(\ y f x -> f (y y f) x)
(\ y f x -> f (y y f) x)
(\ g' gmax lmax list
-> if list == [] then
gmax
else
g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)
In an equation for `msss'':
msss'
= (\ y f x -> f (y y f) x)
(\ y f x -> f (y y f) x)
(\ g' gmax lmax list
-> if list == [] then
gmax
else
g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)
Failed, modules loaded: none.
Changing from f (y y f) to f (y f) just gives
C:\maxsubseq.hs:11:29:
Couldn't match expected type `[Int] -> Int'
with actual type `[Int]'
Expected type: (([Int] -> Int) -> t1 -> t0) -> t2 -> t0
Actual type: ([Int] -> Int) -> t1 -> t0
In the first argument of `y', namely `f'
In the first argument of `f', namely `(y f)'
Failed, modules loaded: none.
I've tried taking a different approach by just defining the combinator externally, however this still isn't working and doesn't really meet my challenge to do it in one expression.
y f = f (y f)
msss' :: [Int] -> Int
msss' = y (\g' gmax lmax list -> if list == []
then gmax
else g' (max gmax lmax + head list)
(max 0 lmax + head list)
tail list)
Can you spot what's wrong with what I'm doing? I'm at a loss. The complaining about constructing infinite types really ticks me off because I though Haskell was all about that sort of thing. It has infinite data structures, so why the problem with infinite types? I suspect it has something to do with that paradox which showed untyped lambda calculus is inconsistent. I'm not sure though. Would be good if someone could clarify.
Also, I'm under the impression that recursion can always be represented with the fold functions. Can anyone show me how I could do it by just using a fold? The requirement that the code be a single expression still stands though.
You cannot define the Y combinator like that in Haskell. As you noticed, that results in an infinite type. Fortunately, it is already available in Data.Function as fix, where it's defined using a let binding:
fix f = let x = f x in x
Because the Y combinator needs infinite types, you'll need workarounds like this one.
But I'd write your msss function as a one-liner like this:
msss = fst . foldr (\x (gmax, lmax) -> let g = max (lmax + x) in (g gmax, g 0)) (0, 0)
Well let's think about it for a minute. What type does this lambda expression have?
(\y f x -> f (y y f) x)
Well f is a function (a -> b) -> a -> b, and x is some value b. What does that make y? Well given what we just said about f,
(y y f) :: (a -> b)
Also, since we are applying this expression to itself, we know that y has the same type as the entire expression. This is the part where I get a little bit stumped.
So y is some magical higher-order function. And it takes two functions as input. So it's sort of like y :: f1 -> f2 -> f3. f2 has the form of f, and f3 has the result type mentioned above.
y :: f1 -> ((a -> b) -> a -> b) -> (a -> b)
The question is...what is f1? Well, it has to be the same as the type of y. Do you see how this is getting beyond the power of Haskell's type system? The type is defined in terms of itself.
f1 = f1 -> ((a -> b) -> a -> b) -> (a -> b)
If you want a self-contained "one-liner", then take hammar's suggestion instead:
msss' = (\f -> let x = f x in x)
(\g' gmax lmax list -> case list of
[] -> gmax
(x:xs) -> g' (max gmax lmax + x) (max 0 lmax + x) xs
) 0 0
Although imho if max is allowable, then fix from Data.Function should be allowable as well. Unless you are in some Prelude-only contest.

Help In Understanding These Statesments In Haskell

What is the meaning of these statements in Haskell:
a)
(\x -> x + 1)
b)
(\x -> x - 2)
c)
(\x -> mod (x * 3) 5)
I understand the x + 1, mod(x * 3) 5 etc but the \x before those statements makes them difficult for me to understand.
thanks for your help
\ and -> define a lambda (you could call it an inline function or a nameless function). So \x->x is the same as \ x -> x is the same as a function which returns its argument. And \x y -> x + y is a function which returns the sum of its two arguments.

Resources