I now understand the type signature of s (s k):
s (s k) :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
And I can create examples that work without error in the Haskell WinGHCi tool:
Example:
s (s k) (\g -> 2) (\x -> 3)
returns 2.
Example:
s (s k) (\g -> g 3) successor
returns 4.
where successor is defined as so:
successor = (\x -> x + 1)
Nonetheless, I still don't have an intuitive feel for what s (s k) does.
The combinator s (s k) takes any two functions f and g. What does s (s k) do with f and g? Would you give me the big picture on what s (s k) does please?
Alright, let's look at what S (S K) means. I'm going to use these definitions:
S = \x y z -> x z (y z)
K = \x y -> x
S (S K) = (\x y z -> x z (y z)) ((\x y z -> x z (y z)) (\a b -> a)) -- rename bound variables in K
= (\x y z -> x z (y z)) (\y z -> (\a b -> a) z (y z)) -- apply S to K
= (\x y z -> x z (y z)) (\y z -> (\b -> z) (y z)) -- apply K to z
= (\x y z -> x z (y z)) (\y z -> z) -- apply (\_ -> z) to (y z)
= (\x y z -> x z (y z)) (\a b -> b) -- rename bound variables
= (\y z -> (\a b -> b) z (y z)) -- apply S to (\a b -> b)
= (\y z -> (\b -> b) (y z)) -- apply (\a b -> b) to z
= (\y z -> y z) -- apply id to (y z)
As you can see, it's just ($) with more specific type.
Related
I am studying Haskell and I am learning what is an abstraction, substitution (beta equivalence), application, free and bound variables (alpha equivalence), but I have some doubts resolving these exercises, I don't know if my solutions are correct.
Make the following substitutions
1. (λ x → y x x) [x:= f z]
Sol. (\x -> y x x) =>α (\w -> y w w) =>α (\w -> x w w) =>β (\w -> f z w w)
2. ((λ x → y x x) x) [y:= x]
Sol. ((\x -> y x x)x) =>α (\w -> y w w)[y:= x] = (\w -> x w w)
3. ((λ x → y x) (λ y → y x) y) [x:= f y]
Sol. aproximation, i don't know how to do it: ((\x -> y x)(\y -> y x) y) =>β
(\x -> y x)y x)[x:= f y] =>β y x [x:= f y] = y f y
4. ((λ x → λ y → y x x) y) [y:= f z]
Sol aproximation, ((\x -> (\y -> (y x x))) y) =>β ((\y -> (y x x)) y) =>α ((\y -> (y x x)) f z)
Another doubt that I have is if can I run these expressions on this website? It is a Lambda Calculus Calculator but I do not know how to run these tests.
1. (λ x → y x x) [x:= f z]
Sol. (\x -> y x x) =>α (\w -> y w w) =>α (\w -> x w w) =>β (\w -> f z w w)
No, you can't rename y, it's free in (λ x → y x x). Only bound variables can be (consistently) α-renamed. But only free variables can be substituted, and there's no free x in that lambda term.
2. ((λ x → y x x) x) [y:= x]
Sol. ((\x -> y x x)x) =>α (\w -> y w w)[y:= x] = (\w -> x w w)
Yes, substituting x for y would allow it to be captured by the λ x, so you indeed must α-rename the x in (λ x → y x x) first to some new unique name as you did, but you've dropped the application to the free x for some reason. You can't just omit parts of a term, so it's ((\w -> y w w) x)[y:= x]. Now perform the substitution. Note you're not asked to perform the β-reduction of the resulting term, just the substitution.
I'll leave the other two out. Just follow the rules carefully. It's easy if you rename all bound names to unique names first, even if the renaming is not strictly required, for instance
((λ x → y x) (λ y → y x) y) [x:= f y] -->
((λ w → y w) (λ z → z x) y) [x:= f y]
The "unique" part includes also the free variables used in the substitution terms, that might get captured after being substituted otherwise (i.e. without the renaming being performed first, in the terms in which they are being substituted). That's why we had to rename the bound y in the above term, -- because y appears free in the substitution term. We didn't have to rename the bound x but it made it easier that way.
I'm trying to find the type of (\x y z -> x . y z) but haven't had any success in finding the same type as ghci
(\x y z -> x . y z) :: (b -> c) -> (t -> a -> b) -> t -> a -> c
After removing the infix functions and getting (\x y z -> (.) x (y z)), I'm not sure how to proceed.
We can expand the expression \x y z -> x . y z as follows:
\x y z -> x . y z
≡ \x y z w -> x (y z w) -- because (f . g) ≡ (\x -> f (g x))
≡ \f g x y -> f (g x y) -- renaming the variables
Now, we can look at the types:
x :: a1
y :: a2
-- Because g is applied to x and y:
g :: (a1 -> a2 -> b)
-- Because f is applied to (g x y):
f :: (b -> c)
-- Therefore:
(\f g x y -> f (g x y)) :: (b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c
-- └──┬───┘ └──────┬──────┘ │ │
-- f g x y
As you can see, this function just composes f and g:
┌───────┐ ┌───────┐
x :: a1 ──┤ │ │ │
│ g ├── b ──┤ f ├── c
y :: a2 ──┤ │ │ │
└───────┘ └───────┘
For more information, take a look at the following question and answer: What does (f .) . g mean in Haskell?
Work backwards, and assign type variables until they are specified:
-- We assign types one by one:
(.) x (y z) :: a -- So,
(y z) :: b
(.) x :: b -> a
x :: c
(.) :: c -> b -> a
But we know that (.) :: (y -> z) -> (x -> y) -> (x -> z), so, we can equate some types, written with ~:
c ~ y -> z
b ~ x -> y
a ~ x -> z
We now know that:
x :: y -> z
(.) x (y z) :: x -> z
y z :: x -> y
So
z :: d
y :: d -> x -> y
Finally, we just complete the type of the function
-- (type of x) (Type of y) (type of z) (return type)
(\x y z -> x . y z) :: (y -> z) -> (d -> x -> y) -> d -> (x -> z)
Finally, we can ask GHCi for confirmation:
Prelude> :t (\x y z -> x . y z)
(\x y z -> x . y z) :: (b -> c) -> (t -> a -> b) -> t -> a -> c
Hello is there a way to write point free style when using infix notation?
f::Int->Int->Int->Int
f a b=(+) (a+b)
Why you cannot do something like this ?
f::Int->Int->Int->Int
f a b=(a+b) +
or
f a b= (a+b) `+`
Can you not combine operators in point free style like e.g?
ptfree::Int->Int->Int->Int
ptfree=(+) (+)
I mean you can chop arguments of functions like fold but why not for operator arguments?
Well since you need to pass two parameters, we can use what is known as the "surprised owl operator". This is basically a composition of parameters. So we can use:
f = ((.).(.)) (+) (+)
Or we can more inline the operator like:
f = ((+) .) . (+)
The owl operator ((.).(.)) f g basically is short for \x y -> f (g x y)
How does this work?
The canonical form of the "surprised owl operator" is:
= ((.) . (.))
------------- (canonical form)
(.) (.) (.)
So we can now replace the (.)s with corresponding lambda expressions:
(\f g x -> f (g x)) (.) (.)
So now we can perform some replacements:
(\f g x -> f (g x)) (.) (.)
-> (\x -> (.) ((.) x))
-> (\x -> (\q r y -> q (r y)) ((.) x))
-> (\x -> (\r y -> ((.) x) (r y)))
-> (\x r y -> ((.) x) (r y))
-> (\x r y -> ((\s t u -> s (t u)) x) (r y))
-> (\x r y -> (\t u -> x (t u)) (r y))
-> (\x r y -> (\u -> x ((r y) u)))
-> \x r y u -> x ((r y) u))
-> \x r y u -> x (r y u)
So basically it means that our surprised owl operator, is equal to:
surprised_owl :: (y -> z) -> (a -> b -> y) -> a -> b -> z
surprised_owl f g x y = f (g x y) -- renamed variables
And if we now specialize this with the fuctions provided (two times (+)), we get:
f = surprised_owl (+) (+)
so:
f x y = (+) ((+) x y)
You must compose (+) with (+) twice, for it to be completely point-free: f = ((+) .) . (+)
Recall that composition is defined as
(f . g) x = f (g x)
or, equivalently:
(f . g) = \x -> f (g x)
So, if you look at the composition f = ((+) .) . (+) and work backwards using the definition of (.):
f = ((+) .) . (+)
f = \x -> ((+) .) ((+) x) -- definition of (.)
f = \y -> (\x -> (+) (((+) x) y)) -- definition of (.)
f x y = (+) (((+) x) y) -- a simpler way to write this
f x y z = (+) (((+) x) y) z -- explicitly add in the final argument (eta expansion)
f x y z = ((+) x y) + z -- rewrite as infix
f x y z = (x + y) + z -- rewrite as infix
and you see we end up with what we started before we tried to make it point-free, so we know that this definition works. Going the other way through the steps above, roughly bottom-to-top, could give you an idea of how you might find such a point-free definition of a function like f.
When you "leave off" multiple arguments from the "end" like this, you usually must compose multiple times. Working through a few similar functions should help build intuition for this.
Note: I wouldn't generally recommend using this sort of point-free (when it complicates things) in production code.
So i know that:
(.) = (f.g) x = f (g x)
And it's type is (B->C)->(A->B)->A->C
But what about:
(.)(.) = _? = _?
How this is represented? I thought of:
(.)(.) = (f.g)(f.g)x = f(g(f(g x))) // this
(.)(.) = (f.g.h)x = f(g(h x)) // or this
But as far as i tried to get type of it, it's not correct to what GHCi tells me.
So what are both "_?"
Also - what does function/operator $ do?
First off, you're being sloppy with your notation.
(.) = (f.g) x = f (g x) -- this isn't true
What is true:
(.) f g x = (f.g) x = f (g x)
(.) = \f g x -> f (g x)
And its type is given by
(.) :: (b -> c) -> (a -> b) -> a -> c
-- n.b. lower case, because they're type *variables*
Meanwhile
(.)(.) :: (a -> b -> d) -> a -> (c -> b) -> c -> d
-- I renamed the variables ghci gave me
Now let's work out
(.)(.) = (\f' g' x' -> f' (g' x')) (\f g x -> f (g x))
= \g' x' -> (\f g x -> f (g x)) (g' x')
= \g' x' -> \g x -> (g' x') (g x)
= \f y -> \g x -> (f y) (g x)
= \f y g x -> f y (g x)
= \f y g x -> (f y . g) x
= \f y g -> f y . g
And ($)?
($) :: (a -> b) -> a -> b
f $ x = f x
($) is just function application. But whereas function application via juxtaposition is high precedence, function application via ($) is low precedence.
square $ 1 + 2 * 3 = square (1 + 2 * 3)
square 1 + 2 * 3 = (square 1) + 2 * 3 -- these lines are different
As dave4420 mentions,
(.) :: (b -> c) -> (a -> b) -> a -> c
So what is the type of (.) (.)? dave4420 skips that part, so here it is: (.) accepts a value of type b -> c as its first argument, so
(.) :: ( b -> c ) -> (a -> b) -> a -> c
(.) :: (d -> e) -> ((f -> d) -> f -> e)
so we have b ~ d->e and c ~ (f -> d) -> f -> e, and the resulting type of (.)(.) is (a -> b) -> a -> c. Substituting, we get
(a -> d -> e) -> a -> (f -> d) -> f -> e
Renaming, we get (a -> b -> c) -> a -> (d -> b) -> d -> c. This is a function f that expects a binary function g, a value x, a unary function h and another value y:
f g x h y = g x (h y)
That's the only way this type can be realized: g x :: b -> c, h y :: b and so g x (h y) :: c, as needed.
Of course in Haskell, a "unary" function is such that expects one or more arguments; similarly a "binary" function is such that expects two or more arguments. But not less than two (so using e.g. succ is out of the question).
We can also tackle this by writing equations, combinators-style1. Equational reasoning is easy:
(.) (.) x y z w q =
((.) . x) y z w q =
(.) (x y) z w q =
(x y . z) w q =
x y (z w) q
We just throw as much variables as needed into the mix and then apply the definition back and forth. q here was an extra, so we can throw it away and get the final definition,
_BB x y z w = x y (z w)
(coincidentally, (.) is known as B-combinator).
1 a b c = (\x -> ... body ...) is equivalent to a b c x = ... body ..., and vice versa, provided that x does not appear among {a,b,c}.
Given the following example
foldr(\ x y -> ........
if the input is a list for example [1,2,3]
what is x and what is y?
Let's take a look at the type for foldr.
foldr :: (a -> b -> b) -> b -> [a] -> b
Since you're providing the function that uses x and y, you can see from the type that x will be a value from your list ([1,2,3]), and y must be the accumulator value, which you initialize with the second parameter to foldr.
The definition of foldr is
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
You can just apply it directly in your example:
foldr (\x y -> foo x y) z [1,2,3]
=
(\x y -> foo x y) 1 (foldr (\x y -> foo x y) z [2,3])
=
foo 1 (foldr (\x y -> foo x y) z [2,3])
So x is 1 and y is foldr (\x y ...) z [2,3]).
In general you can think of foldr f z as replacing every (:) in a list with f, and the [] with z. So foldr f z [a,b,c,d] = f a (f b (f c (f d z))) (since [a,b,c,d] = (:) a ((:) b ((:) c ((:) d []))).