I'm doing some excersises where I have to add a function's type and explain what it does. I'm stuck with this:
phy = uncurry ($)
The type, according to GHCi is phy :: (a -> b, a) -> b. My haskell knowledge is basic so I really have no idea what it does.
Let's spell out the type part systematically. We'll start with the types of uncurry and ($):
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
Since the target expression has ($) as the argument of uncurry, let's line up their types to reflect this:
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
The whole type of ($) lines up with the first argument type of uncurry, and the argument and result types of ($) line up with those of uncurry's first argument as shown. This is the correspondence:
uncurry's a <==> ($)'s a -> b
uncurry's b <==> ($)'s a
uncurry's c <==> ($)'s b
This is kinda confusing, because the a and b type variables in one type are not the same as in the other (just like the x in plusTwo x = x + 2 is not the same as the x in timesTwo x = x * 2). But we can rewrite the types to help up reason about this. In simple Haskell type signatures like this, any time you see a type variable you can replace all of its occurrences with any other type get a valid type as well. If you pick fresh type variables (type variables that don't appear anywhere in the original), you get an equivalent type (one that can be converted back to the original); if you pick a non-fresh type you get a specialized version of the original that works with a narrower range of types.
But anyway, let's apply this to the type of uncurry::
-- Substitute a ==> x, b ==> y, c ==> z:
uncurry :: (x -> y -> z) -> (x, y) -> z
Let's redo the "line up" using the rewritten type:
uncurry :: (x -> y -> z) -> (x, y) -> z
($) :: (a -> b) -> a -> b
Now it's obvious: x <==> a -> b, y <==> a and z <==> b. Now, substituting uncurry's type variables for their counterpart types in ($), we get:
uncurry :: ((a -> b) -> a -> b) -> (a -> b, a) -> b
($) :: (a -> b) -> a -> b
And finally:
uncurry ($) :: (a -> b, a) -> b
So that's how you figure out the type. How about what it does? Well, the best way to do that in this case is to look at the type and think about it carefully, figuring out what we'd have to write to get a function of that type. Let's rewrite it this way to make it more mysterious:
mystery :: (a -> b, a) -> b
mystery = ...
Since we know mystery is a function of one argument, we can expand this definition to reflect that:
mystery x = ...
We also know that its argument is a pair, so we can expand a bit more:
mystery (x, y) = ...
Since we know that x is a function and y :: a, I like to use f to mean "function" and to name variables the same as their type—it helps me reason about the functions, so let's do that:
mystery (f, a) = ...
Now, what do we put in the right hand side? We know it must be of type b, but we don't know what type b is (it's actually whatever the caller chooses, so we can't know). So we must somehow make a b using our function f :: a -> b and value a :: a. Aha! We can just call the function with the value:
mystery (f, a) = f a
We wrote this function without looking at uncurry ($), but it turns out that it does the same thing as uncurry ($) does, and we can prove it. Let's start with the definitions of uncurry and ($):
uncurry f (a, b) = f a b
f $ a = f a
Now, substituting equals for equals:
uncurry ($) (f, a) = ($) f a -- definition of uncurry, left to right
= f $ a -- Haskell syntax rule
= f a -- definition of ($), left to right
= mystery (f, a) -- definition of mystery, right to left
So one way to attack a type that you don't understand in Haskell is to just try and write some code that has that type. Haskell is different from other languages in that very often this is a better strategy than trying to read the code.
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
uncurry ($) :: (a -> b, a) -> b
If you inspect types of uncurry and $ and its description:
uncurry converts a curried function to a function on pairs.
All it does is it takes a function (a -> b -> c) and returns a function that takes the parameters as a tuple.
So phy does the same thing as $, but instead of f $ x or ($) f x you call it like phy (f, x).
The other two answers are fine. I just have a slightly different take on it.
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
Since the "->" in type signatures associates to the right, I can equivalently write these two type signatures like this:
uncurry :: (a -> b -> c) -> ((a, b) -> c)
($) :: (a -> b) -> (a -> b)
uncurry takes an arbitrary function of two inputs and changes it into a funciton of one argument where that argument is a tuple of the original two arguments.
($) takes a simple one-argument function and turns it into...itself. Its only effect is syntactical. f $ is equivalent to f.
(Make sure you understand higher-order functions and currying, read Learn You a Haskell chapter on higher-order functions, then read difference between . (dot) and $ (dollar sign) and function composition (.) and function application ($) idioms)
($) is just a function application, f $ x is equivalent to f x. But that's good, because we can use explicit function application, for example:
map ($2) $ map ($3) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0]
which is equivalent to:
map (($2) . ($3)) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0]
Check the type of ($): ($) :: (a -> b) -> a -> b. You know that type declarations are right-associative, therfore the type of ($) can also be written as (a -> b) -> (a -> b). Wait a second, what's that? A function that receives an unary function and returns an unary function of the same type? This looks like a particular version of an identity function id :: a -> a. Ok, some types first:
($) :: (a -> b) -> a -> b
id :: a -> a
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry ($) :: (b -> c, b) -> c
uncurry id :: (b -> c, b) -> c
When coding Haskell, always look at types, they give you lots of information before you even look at the code. So, what's a ($)? It's a function of 2 arguments. What's an uncurry? It's a function of 2 arguments too, the first being a function of 2 arguments. So uncurry ($) should typecheck, because 1st argument of uncurry should be a function of 2 arguments, which ($) is. Now try to guess the type of uncurry ($). If ($)'s type is (a -> b) -> a -> b, substitute it for (a -> b -> c): a becomes (a -> b), b becomes a, c becomes b, therefore, uncurry ($) returns a function of type ((a -> b), a) -> b. Or (b -> c, b) -> c as above, which is the same thing. So what does that type tells us? uncurry ($) accepts a tuple (function, value). Now try to guess what's it do from the type alone.
Now, before the answer, an interlude. Haskell is so strongly typed, that it forbids to return a value of a concrete type, if the type declaration has a type variable as a return value type. So if you have a function with a type a -> b, you can't return String. This makes sense, because if your function's type was a -> a and you always returned String, how would user be able to pass a value of any other type? You should either have a type String -> String or have a type a -> a and return a value that depends solely on an input variable. But this restriction also means that it is impossible to write a function for certain types. There is no function with type a -> b, because no one knows, what concrete type should be instead of b. Or [a] -> a, you know that this function can't be total, because user can pass an empty list, and what would the function return in that case? Type a should depend on a type inside the list, but the list has no “inside”, its empty, so you don't know what is the type of elements inside empty list. This restriction allows only for a very narrow elbow room for possible functions under a certain type, and this is why you get so much information about a function's possible behavior just by reading the type.
uncurry ($) returns something of type c, but it's a type variable, not a concrete type, so its value depends on something that is also of type c. And we see from type declaration that the function in the tuple returns values of type c. And the same function asks for a value of type b, which can only be found in the same tuple. There are no concrete types nor typeclasses, so the only thing uncurry ($) can do is to take the snd of a tuple, put it as an argument in function in fst of a tuple, return whatever it returns:
uncurry ($) ((+2), 2) -- 4
uncurry ($) (head, [1,2,3]) -- 1
uncurry ($) (map (+1), [1,2,3]) -- [2,3,4]
There is a cute program djinn that generates Haskell programs based on types. Play with it to see that our type guesses of uncurry ($)'s functionality is correct:
Djinn> f ? a -> a
f :: a -> a
f a = a
Djinn> f ? a -> b
-- f cannot be realized.
Djinn> f ? (b -> c, b) -> c
f :: (b -> c, b) -> c
f (a, b) = a b
This shows, also, that fst and snd are the only functions that can have their respective types:
Djinn> f ? (a, b) -> a
f :: (a, b) -> a
f (a, _) = a
Djinn> f ? (a, b) -> b
f :: (a, b) -> b
f (_, a) = a
Related
I came across a usage of the . operator that I don't quite understand.
I tried to reason about it myself, but the conclusion I reach is different from what GHCI produces.
I'm using :t to inspect the type of the expression.
The functions I'm using are the last and (.), which have the following signatures:
last :: [a] -> a
(.) :: (b -> c) -> (a -> b) -> a -> c
The function I am confused about is this:
(last .)
I am not sure what this construct is, but I assumed that it would be similar to function composition.
Using my reasoning, I would expect this to produce the following function:
(last .) :: (b -> [c]) -> (a -> b) -> a -> [c]
What :t actually gives me is this:
(last .) :: (a -> [c]) -> a -> c
This is an example of infix operator sectioning [Haskell-wiki]:
(...)
(2^) (left section) is equivalent to (^) 2, or more verbosely \x -> 2 ^ x.
So we here constructed a function that looks like:
\f -> last . f
or shorter:
(.) last
The (.) :: (b -> c) -> (a -> b) -> a -> c function takes two functions g and h, and creates a function \x -> g (h x). Here g is thus last.
We thus created a function that takes as input a function f :: b -> [c], that then returns last . f.
This is yet another Haskell-through-category-theory question.
Let's take something simple and well-known as an example. fmap?
So fmap :: (a -> b) -> f a -> f b, omitting the fact that f is actually a Functor. As far as I understand, (a -> b) -> f a -> f b is nothing but a syntax sugar for the (a -> b) -> (f a -> f b); hence conclusion:
(1) fmap is a function producing a function.
Now, Hask contains functions as well, so (a -> b) and, in particular, (f a -> f b) is an object of the Hask (because objects of the Hask are well-defined Haskell types - a-ka mathematical sets - and there indeed exists set of type (a -> b) for each possible a, right?). So, once again:
(2) (a -> b) is an object of the Hask.
Now weird thing happens: fmap, obviously, is a morphism of the Hask, so it is a function, that takes another function and transform it to a yet another function; final function hasn't been applied yet.
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b. For each item i of type a there exists a morphism apply_i :: (f a -> f b) -> f b defined as \f -> f (lift i), where lift i is a way to build an f a with particular i inside.
The other way to see it is GHC-style: (a -> b) -> f a -> f b. On the contrast with what I've written above, (a -> b) -> f a is mapping to the regular object of the Hask. But such a view contradicts fundamental Haskell's axiom - no multivariate functions, but applied (curried) alternatives.
I'd like to ask at this point: is (a -> b) -> f a -> f b suppose to be an (a -> b) -> (f a -> f b) -> f b, sugared for simplicity, or am I missing something really, really important there?
is (a -> b) -> f a -> f b suppose to be an (a -> b) -> (f a -> f b) -> f b, sugared for simplicity
No. I think what you're missing, and it's not really your fault, is that it's only a very special case that the middle arrow in (a -> b) -> (f a -> f b) can be called morphism in the same way as the outer (a -> b) -> (f a -> f b) can. The general case of a Functor class would be (in pseudo-syntax)
class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
fmap :: (a ──> b) -> f a ~> f b
So, it maps morphisms in the category whose arrows are denoted ──> to morphisms in the category ~>, but this morphism-mapping itself is just plainly a function. Your right, in Hask specifically function-arrows are the same sort of arrows as the morphism arrows, but this is mathematically speaking a rather degenerate scenario.
fmap is actually an entire family of morphisms. A morphism in Hask is always from a concrete type to another concrete type. You can think of a function as a morphism if the function has a concrete argument type and a concrete return type. A function of type Int -> Int represents a morphism (an endomorphism, really) from Int to Int in Hask. fmap, however has type Functor f => (a -> b) -> f a -> f b. Not a concrete type in sight! We just have type variables and a quasi-operator => to deal with.
Consider the following set of concrete function types.
Int -> Int
Char -> Int
Int -> Char
Char -> Char
Further, consider the following type constructors
[]
Maybe
[] applied to Int returns a type we could call List-of-Ints, but we usually just call [Int]. (One of the most confusing things about functors when I started out was that we just don't have separate names to refer to the types that various type constructors produce; the output is just named by the expression that evaluates to it.) Maybe Int returns the type we just call, well, Maybe Int.
Now, we can define a bunch of functions like the following
fmap_int_int_list :: (Int -> Int) -> [Int] -> [Int]
fmap_int_char_list :: (Int -> Char) -> [Int] -> [Char]
fmap_char_int_list :: (Char -> Int) -> [Char] -> [Int]
fmap_char_char_list :: (Char -> Char) -> [Char] -> [Char]
fmap_int_int_maybe :: (Int -> Int) -> Maybe Int -> Maybe Int
fmap_int_char_maybe :: (Int -> Char) -> Maybe Int -> Maybe Char
fmap_char_int_maybe:: (Char -> Int) -> Maybe Char -> Maybe Int
fmap_char_char_maybe :: (Char -> Char) -> Maybe Char -> Maybe Char
Each of these is a distinct morphism in Hask, but when we define them in Haskell, there's a lot of repetition.
fmap_int_int_list f xs = map f xs
fmap_int_char_list f xs = map f xs
fmap_char_int_list f xs = map f xs
fmap_char_char_list f xs = map f xs
fmap_int_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_int_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
The definitions don't differ when the type of f differs, only when the type of x/xs differs. That means we can define the following polymorphic functions
fmap_a_b_list f xs = map f xs
fmap_a_b_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
each of which represents a set of morphisms in Hask.
fmap itself is an umbrella term we use to refer to constructor-specific morphisms referred to by all the polymorphic functions.
With that out of the way, we can better understand fmap :: Functor f => (a -> b) -> f a -> f b.
Given fmap f, we first look at the type of f. We might find out, for example, that f :: Int -> Int, which means fmap f has to return one of fmap_int_int_list or fmap_int_int_maybe, but we're not sure which yet. So instead, it returns a constrained function of type Functor f => (Int -> Int) -> f Int -> f Int. Once that function is applied to a value of type [Int] or Maybe Int, we'll finally have enough information to know which morphism is actually meant.
Now weird thing happens: fmap, obviously, is a morphism of the Hask, so it is a function, that takes another function and transform it to a yet another function; final function hasn't been applied yet.
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b. For each item i of type a there exists a morphism apply_i :: (f a -> f b) -> f b defined as \f -> f (lift i), where lift i is a way to build an f a with particular i inside.
The notion of application in category theory is modelled in the form of CCC's - Cartesian Closed Categories. A category 𝓒 is a CCC if you have a natural bijection 𝓒(X×Y,Z) ≅ 𝓒(X,Y⇒Z).
In particular this implies that there exists a natural transformation 𝜺 (the evaluation), where 𝜺[Y,Z]:(Y⇒Z)×Y→Z, such that for every g:X×Y→Z there exists a 𝝀g:X→(Y⇒Z) such that, g = 𝝀g×id;𝜺[Y,Z]. So when you say,
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b.
The way you go from (f a -> f b) to the f b, or using the notation above, from (f a ⇒ f b) is via 𝜺[f a,f b]:(f a ⇒ f b) × f a → f b.
The other important point to keep in mind is that in Category Theory "elements" are not primitive concepts. Rather an element is an arrow of the form 𝟏→X,where 𝟏 is the terminal object. If you take X=𝟏 you have that 𝓒(Y,Z) ≅ 𝓒(𝟏×Y,Z) ≅ 𝓒(𝟏,Y⇒Z). That is, the morphisms g:Y→Z are in bijection to elements 𝝀g:𝟏→(Y⇒Z).
In Haskell this means functions are precisely the "elements" of arrow types. So in Haskell an application h y would be modelled via the evaluation of 𝝀h:𝟏→(Y⇒Z) on y:𝟏→Y. That is, the evaluation of (𝝀h)×y:𝟏→(Y⇒Z)×Y, which is given by the composition (𝝀h)×y;𝜺[Y,Z]:𝟏→Z.
For the sake of completeness, this answer focuses on a point that was addressed in various comments, but not by the the other answers.
The other way to see it is GHC-style: (a -> b) -> f a -> f b. On the contrast with what I've written above, (a -> b) -> f a is mapping to the regular object of the Hask.
-> in type signatures is right-associative. That being so, (a -> b) -> f a -> f b is really the same as (a -> b) -> (f a -> f b), and seeing (a -> b) -> f a in it would be a syntactic mix-up. It is no different from how...
(++) :: [a] -> [a] -> [a]
... doesn't mean that partially applying (++) will give us an [a] list (rather, it gives us a function that prepends some list).
From this point of view, the category theory questions you raise (for instance, on "need[ing] one more Hask's morphism to get from the (f a -> f b) to the f b") are a separate matter, addressed well by Jorge Adriano's answer.
I have the applicative <$> operator more or less figured out, but I can't understand the signature I'm getting with the following example:
ghci> let f x y z = x + y + z -- f::Num a => a -> a -> a -> a
ghci> f <$> Just 2 <*> Just 3 <*> Just 4
Just 9
This result I understand, but when checking the following type:
ghci> :t (<$> f)
(<$> f) :: Num a => ((a -> a -> a) -> b) -> a -> b --This makes no sense to me
That signature I would understand as : a function that takes a (a -> a- > a) -> b function and an a as parameters and returns a b. According to this reasoning , I should call this like :
(<$>f) f 4
which would result in an Integer.
Obviously this is not true, so can you please help me understand how to read the type of (<$> f)?
a function that takes a (a -> a- > a) -> b function and an a as parameters and returns a b.
This is correct.
According to this reasoning , I should call this like :
(<$>f) f 4
which would result in an Integer.
No, because f does not have type (a -> a -> a) -> b or one compatible with it. Instead it has type Num a => a -> a -> a -> a. That is, f takes three numbers and produces a number, whereas we're looking for a function that takes a function (of type a -> a -> a) as its first argument.
<$> takes as a second argument something of type g b, where g is any applicative functor.
You are passing f :: Num a => a -> a -> a -> a as a second argument. Let's ignore the Num a context to keep things simple.
Hence, we look for g,b such that g b = a -> a -> a -> a.
Let's write the type of f in prefix form:
f :: (->) a ((->) a ((->) a a)) = g b
Hence, g = (->) a and b = ((->) a ((->) a a)). The latter is b = a -> a -> a in infix form.
It happens that (->) a is an applicative functor, so <$> f type checks. Note however that <$> is used on a completely different functor than the Maybe one you were using in your examples. Hence the confusion.
TL;DR: overloaded identifiers can shapeshift to many things adapting to their contexts, possibly in some unexpected way.
Given:
uncurry :: (a-> b -> c) -> (a,b) -> c
id :: a -> a
Invoking uncurry id results in a function of type: (b -> c, b) -> c
How do we get this result?
How can you use id (a -> a) as the first parameter to uncurry, which requires a (a -> b -> c) function?
It's easier to understand if we try and look at it from the point of making the types work out: figuring out what we need to do to id's type to get it to fit the shape required by uncurry. Since we have:
id :: a -> a
we also have:
id :: (b -> c) -> (b -> c)
This can be seen by substituting b -> c for a in the original type of id, just as you might substitute Int instead when figuring out the type of id 42. We can then drop the parentheses on the right-hand side, since (->) is right-associative:
id :: (b -> c) -> b -> c
showing that id's type fits the form a -> b -> c, where a is b -> c. In other words, we can reshape id's type to fit the required form simply by specialising the general type it already has.
Another way to understand this is to see that uncurry ($) also has the type (b -> c, b) -> c. Comparing the definitions of id and ($):
id :: a -> a
id a = a
($) :: (a -> b) -> a -> b
($) f x = f x
we can make the latter definition more point-free:
($) f = f
at which point the fact that ($) is simply a specialisation of id to a more specific type becomes clear.
How can you use id (a -> a) as the first parameter to uncurry, which requires a (a -> b -> c) function?
Actually, uncurry requires (a -> (b -> c)) function. Can you spot the difference? :)
Omitting parentheses is evil (well, sometimes). It makes it impossible for a novice to decipher Haskell. Of course after you've gathered some experience with the language, you feel like you don't need them at all, anymore.
Here, it all becomes clear once we write out all the omitted parentheses back explicitly:
uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id :: a -> a
Now, writing uncurry id calls for a type unification of a1 -> a1 with a2 -> (b -> c). This is straightforward, a1 ~ a2 and a1 ~ (b -> c). Just mechanical stuff, no creative thinking involved here. So id in question actually has type a -> a where a ~ (b -> c), and so uncurry id has type (b -> c,b) -> c, by simple substitution of a ~ (b -> c) into (a,b) -> c. That is, it expects a pair of a b -> c function and a b value, and must produce a c value.
Since the types are most general (i.e. nothing is known about them, and so there's no specific functions to call that might do the trick in some special way), the only way to produce a c value here is to call the b -> c function with the b value as an argument. Naturally, that's what ($) does. So uncurry id == uncurry ($), although id is most certainly not ($).
So I was playing around with Haskell today, thinking about autogeneration of function definitions given a type.
For example, the definition of the function
twoply :: (a -> b, a -> c) -> a -> (b, c)
is obvious to me given the type (if I rule out use of undefined :: a).
So then I came up with the following:
¢ :: a -> (a ->b) -> b
¢ = flip ($)
Which has the interesting property that
(¢) ¢ ($) :: a -> (a -> b) -> b
Which brings me to my question. Given the relation =::= for "has the same type as", does the statement x =::= x x ($) uniquely define the type of x? Must x =::= ¢, or does there exist another possible type for x?
I've tried to work backward from x =::= x x ($) to deduce x :: a -> (a -> b) -> b, but gotten bogged down.
x =::= x x ($) is also true for x = const, which has the type a -> b -> a. So it does not uniquely identify the type.
I'd just like add that you should look at http://hackage.haskell.org/package/djinn. It can take many type signatures and derive an implementation from them. If there's only one implementation possible for a type that djinn understands, it will produce it.
From the equation above, we can determine some of a type signature for x. X need not have this type, but it needs to at least unify with this type.
$ :: forall a b. (a -> b) -> a -> b
x :: t1 -> ((a -> b) -> a -> b) -> t1
Given that, it should be straightforward to write a multitude of implementations of x.