In understand the meaning and the type of (\x y z -> x+y+z) for example. The type of (\x y z -> x y z)is (t1 -> t2 -> t3) -> t1 -> t2 -> t3. But what does that mean and why are now round brackets in the type?
In Haskell, something of the form a b c (i.e., juxtaposition of letter-names without infix symbols between them) generally means a is a function and being applied to b and c.In this case, x y z is basically what in other languages would be written x(y,z). In Haskell, you can also write it
\x y z -> x(y)(z)
or in fact (x)(y)(z) or ((x)y)z, but parentheses are discouraged unless needed.
So knowing that, it's clear that the type of the first argument of that lambda, i.e. of x, needs to be a function type. In this case, a function taking two arguments of whatever type y and z happen to be, respectively. I.e.
(\x y z -> x y z) :: (typeofy -> typeofz -> resulttype) -> ...
└───────── type of x ────────────┘
Now say y :: t1 and z :: t2 and the final result should be t3, and we have
(\x y z -> x y z) :: (t1 -> t2 -> t3) -> (t1 -> t2 -> t3)
└─ type of x ──┘ └y └z └(x y z)
Function-arrows are right-associative, i.e. the parentheses on the right side of the type can be omitted:
(\x y z -> x y z) :: (t1 -> t2 -> t3) -> t1 -> t2 -> t3
└─ type of x ──┘ └y └z └(x y z)
The parentheses on the left can not be omitted: t1->t2->t3 -> t1->t2->t3 would actually parse as t1->(t2->(t3->(t1->(t2->t3)))). That would be instead the type of
(\w x y w' x' -> y) :: t1 -> t2 -> t3 -> t1 -> t2 -> t3
└w └x └y └w' └x' └y
...preferrably written (\_ _ y _ _ -> y).
Related
I would like to understand how to get manual to the correct type of this Haskell expression.
(\x y z -> (x y) z)
In general I understand roughly how to manually determine the correct type, but with lambda expressions I am totally confused.
There's a fast way to get the type and a slow way. The slow way is below and does not remove any unnecessary parentheses.
The fast way (for this function)
Let us remember the properties of Haskell functions, namely for
f :: a -> b -> c
Both f x y and (f x) y are the same, as a -> b -> c is a -> (b -> c).
Thus \x y z -> (x y) z is the same as \x y z -> x y z. Therefore, if y's and z's type where a and b resp., then x's type is a -> b -> c:
\x y z -> x y z :: (a -> b -> c) -> a -> b -> c
If we named that function, then we would write:
f :: (a -> b -> c) -> a -> b -> c
f x y z = x y z
or even simpler:
f :: (a -> b -> c) -> a -> b -> c
f = id
as it is the identify function restrained to a function type. You can add parentheses around a -> b -> c in the type to see that, as a -> b -> c is a -> (b -> c):
f :: (a -> b -> c) -> (a -> b -> c)
f = id
The "slow" way
Let's start first by giving the lambda a name. This will enable us to use type signatures and continue from there:
f = \x y z -> (x y) z
Next, we use the regular function syntax instead of the lambda syntax:
f :: A -> B -> C -> D
f x y z = (x y) z
Now, we need to figure out A, B, C and D. Due to x being applied to y, we notice that A must be some function type, e.g. B -> ...?. Next we notice that x y must also be a function. Therefore, A needs to be B -> G, and G needs to be a function again. Since G is able to use z and therefore a value of type C, we know that G is G = C -> D:
f :: (B -> (C -> D)) -> B -> C -> D
We now have x's type, it's B -> (C -> D). Since neither y nor z are restrained, we can now change all type placeholders into type varialbles and end up with:
f :: (b -> (c -> d)) -> b -> c -> d
f x y z = (x y) z
For a last step, let's rename those variables:
f :: (a -> (b -> c)) -> a -> b -> c
f x y z = (x y) z
And that's \x y z -> (x y) z's type: (a -> (b -> c)) -> a -> b -> c.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Just looking for an explanation of the type of this function, please
f x y z = x y (y z)
Prelude says is
f :: ((b -> a) -> a -> c) -> (b -> a) -> b -> c
But I'm not able getting that result with any known method ¬¬
Best regards.
Ugh, those “manually typecheck this expression” exercises are so silly. I don't know why lecturers keep putting them in assignments.
In practice, what's actually much more relevant is to go the other way around: given a type, find an implementation. So let me answer the question from that angle: you already know
f :: ((b -> a) -> a -> c) -> (b -> a) -> b -> c
-- └──────────────────┘ └──────┘ └─┘
...so you have three arguments to accept, reasonable enough to call them x, y and z
f x y z = _
You're in the end looking for a c, and the only c you have available is in the final result of x
x :: (b -> a) -> a -> c
-- └──────┘ └─┘
...which requires two arguments
f x y z = x _ _
...of types b -> a and a. Let's check what we have available
y :: b -> a
z :: b
Great, we can directly use y as the first argument
f x y z = x y _
For the second argument we need an a. Well, z is a b, that doesn't work, but y yields an a when given a b, so we can pass y z and thus end up with
f x y z = x y (y z)
or as we prefer to write it in Haskell, f x y = x y . y.
Now for the other way around: let's start with the η-reduced form
f x y = x y . y
This is a pipeline, so let's start giving the passing-through argument a name p
x y . y :: p -> _
That argument is passed first to y, so we have
y :: p -> _
from which it follows, since y is also the first argument to x
x :: (p -> _) -> _
Furthermore, x then accepts (in the pipeline) whatever comes out of y
y :: p -> r
x :: (p -> r) -> r -> _
Let's call the final result q
y :: p -> r
x :: (p -> r) -> r -> q
And write up the whole function as given:
(\x y -> x y . y) :: ((p -> r) -> r -> q) -> (p -> r) -> p -> q
-- └─────────x────────┘ └──y───┘
Which is, after renaming the type variables, the same as what you started with.
From f x y z = x y (y z), you can reason as follows:
We don't apply z to anything, so it could have some arbitrary type; call it u1 (for unknown number one).
y is applied to z, so its argument type is u1, but the return type is another unknown type u2. Thus y :: u1 -> u2.
y z thus has type u2.
x is applied to y and y z, and so must have a type like (u1 -> u2) -> u2 -> u3; there's no reason to assume that either argument has the same type, or that either type is the same as the return type.
The return value of f is the same as the return value of x, i.e. u3.
Putting it altogether, we have
f :: ((u1 -> u2) -> u2 -> u3) -> (u1 -> u2) -> u1 -> u3
------------------------ ---------- -- --
type of x type of y type of z end result of f
Because the names of the individual type variables aren't important aside from keeping distinct types separate, we can rename them using
u1 ~ b
u2 ~ a
u3 ~ c
to get the type you looked up
f :: ((b -> a) -> a -> c) -> (b -> a) -> b -> c
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.
Just trying to see the types of some lambda expressions like this one:
:t \x -> (\y -> x y)
\x -> (\y -> x y) :: (t1 -> t2) -> t1 -> t2
shouldn't the type here be t1->(t2->t1->t2) ?
Similarly
:t \x -> (\y -> (\k -> y (x k)))
\x -> (\y -> (\k -> y (x k)))
:: (t1 -> t2) -> (t2 -> t3) -> t1 -> t3
Shouldn't the type be t1->(t2->(t3->t2))?
:t \x -> (\y -> x y)
\x -> (\y -> x y) :: (t1 -> t2) -> t1 -> t2
shouldn't the type here be t1->(t2->t1->t2) ?
No, t1->(t2->t1->t2) is the same as t1->t2->t1->t2 which is the type of a three-arguments function (of type t1, t2, and t1) returning t2. However, there are only two lambdas, for the two arguments x and y.
The right type is instead
typeOfX -> (typeofY -> typeOfResult)
\x -> (\y -> x y)
(By the way, none of the parentheses above are needed.)
What is typeOfResult? Is is the type of x y, so it is the return type for x which must be a function.
In order for the code to type check, we must then have that typeOfX is a function type, say a -> b. In such case we can see that typeOfResult = b. Further, in x y we pass y to x, and this can type check only if typeOfY = a.
So,
typeOfX -> typeofY -> typeOfResult
=
(a -> b) -> a -> b
The compiler used names t1 and t2, but this is the same type.
Parentheses here matter, since we must remember that x is a function a -> b.
Without parentheses we would get a three-argument function, as explained above.
You can try to apply the same reasoning to the second example. Start from
typeOfX -> typeofY -> typeOfK -> TypeOfResult, and slowly discover what these types actually are.
The type of x in \x -> \y -> x y is t1 -> t2, and it's the first argument.
As the outermost lambda, it gets applied first, followed by y
You could've written it as \x y -> x y which is just function application in the natural order.
I'm currently having Haskell for university. Given the following haskell code:
true::t -> t1 -> t
true = (\x y -> x)
false::t -> t1 -> t1
false = (\x y -> y)
-- Implication
(==>) = (\x y -> x y true)
The task is to determine the type of the function (==>).
GHCi says it is (==>) :: (t1 -> (t2 -> t3 -> t2) -> t) -> t1 -> t.
I can see that the evaluation order is the following (as the type stays the same):
(==>) = (\x y -> (x y) true)
So the function true ist argument to (x y).
Can anyone explain why the result type t is bound to the result of the first argument and in which way GHCi determines the type of (==>)?
First, to give a better overview,
type True t f = t -> f -> t
type False t f = t -> f -> f
Let's call the result of the implication r, then we have, in \x y -> x y true :: r, that
x y :: True t f -> r
so x :: y -> True t f -> r, and thus
(==>) :: (y -> True t f -> r) -> y -> r
which, expanding True again, is
(==>) :: (y -> (t->f->t) -> r) -> y -> r