What does uncurry ($) do?

I am doing some exercises where I need to add a function type and explain what it does. I am stuck on this:

phy = uncurry ($) 

The type, according to GHCi, is phy :: (a -> b, a) -> b . My haskell knowledge is basic, so I really don't know what it does.

+12
functional-programming haskell currying
Apr 13 '13 at 21:34
source share
4 answers

Let me describe part of the type systematically. We start with the uncurry and ($) types:

 uncurry :: (a -> b -> c) -> (a, b) -> c ($) :: (a -> b) -> a -> b 

Since the target expression has ($) as the uncurry argument, line up their types to reflect this:

 uncurry :: (a -> b -> c) -> (a, b) -> c ($) :: (a -> b) -> a -> b 

The entire type ($) lined up with the first type of the uncurry argument, and the arguments and result types ($) match the parameters of the first uncurry argument, as shown. This is a match:

 uncurry a <==> ($) a -> b uncurry b <==> ($) a uncurry c <==> ($) b 

This is confusing because variables of type a and b in one type are not the same as in the other (just like x in plusTwo x = x + 2 does not match x in timesTwo x = x * 2 ). But we can rewrite the types to help sort this out. In simple Haskell type signatures like this, any time you see a type variable, you can replace all its occurrences with some other type, and also get a valid type. If you select new type variables (enter variables that are not displayed anywhere in the original), you will get an equivalent type (one that can be converted back to the original); if you choose a non-fresh type, you will get a specialized version of the original that works with a narrower range of types.

But anyway, apply this to the uncurry :: type

 -- Substitute a ==> x, b ==> y, c ==> z: uncurry :: (x -> y -> z) -> (x, y) -> z 

Repeat the "line up" command 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, replacing the variables of type uncurry for their partner 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 how do you figure out the type. What about what he does? Well, the best way to do this in this case is to look at the type and think about it carefully, find out what we need to write in order to get a function of this type. Rewrite it in such a way as to make it more mysterious:

 mystery :: (a -> b, a) -> b mystery = ... 

Since we know that mystery is a function of a single argument, we can extend this definition to reflect this:

 mystery x = ... 

We also know that his argument is a pair, so we can expand a little more:

 mystery (x, y) = ... 

Since we know that x is a function and y :: a , I like to use f to denote “functions” and to denote variables in the same way as their type — this helps me reason about functions, so let's do this:

 mystery (f, a) = ... 

Now, what do we put in the right side? We know that this must be of type b , but we do not know what type of b (this is actually what the caller chooses, so we cannot know). Therefore, we must somehow make b using our function f :: a -> b and the value a :: a . Yeah! We can just call the function with the value:

 mystery (f, a) = fa 

We wrote this function without looking at uncurry ($) , but it turns out that it does the same thing as uncurry ($) , and we can prove it. Let's start with the definitions of uncurry and ($) :

 uncurry f (a, b) = fab f $ a = fa 

Now, replacing equals with equals:

 uncurry ($) (f, a) = ($) fa -- definition of uncurry, left to right = f $ a -- Haskell syntax rule = fa -- 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 simply try to write code that has that type. Haskell differs from other languages ​​in that it is often a better strategy than trying to read code.

+12
Apr 13 '13 at 23:37
source share
 uncurry :: (a -> b -> c) -> (a, b) -> c ($) :: (a -> b) -> a -> b uncurry ($) :: (a -> b, a) -> b 

If you check the uncurry and $ types and its description:

uncurry converts a traced function to a function on pairs.

All he does is function (a -> b -> c) and return a function that takes parameters as a tuple.

So phy does the same as $ , but instead of f $ x or ($) fx you call it phy (f, x) .

+9
Apr 13
source share

The other two answers are good. I just changed it a bit.

 uncurry :: (a -> b -> c) -> (a, b) -> c ($) :: (a -> b) -> a -> b 

Since the "->" character in the type is associated on the right, I can equivalently write these two type signatures as follows:

 uncurry :: (a -> b -> c) -> ((a, b) -> c) ($) :: (a -> b) -> (a -> b) 

uncurry takes an arbitrary function of two inputs and turns it into a functional of one argument, where this argument is a tuple of the original two arguments.

($) takes a simple function with one argument and turns it into ... itself. Its only effect is syntactic. f $ equivalent to f .

+4
Apr 14 '13 at 14:29
source share

(Make sure you understand higher order functions and currying, read “Learn You the Haskell” in the higher order function chapter, then read the difference between. (Dots) and $ (dollar sign) and (.) And application ( $) idioms )

($) is just a function application, f $ x equivalent to fx . But this is good, because we can use an 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 ($) : ($) :: (a -> b) -> a -> b . You know that type declarations are right-associative, so type ($) can also be written as (a -> b) -> (a -> b) . Wait a second, what is it? A function that receives a unary function and returns a unary function of the same type? This is similar to the specific version of the id :: a -> a authentication function. Ok, first some types:

 ($) :: (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 a lot of information before you even look at the code. So what is a ($) ? This is a function of two arguments. What is uncurry ? This is a function of 2 arguments, the first of which is a function of two arguments. Thus, uncurry ($) should look like typecheck, because the 1 st argument of uncurry should be a function of 2 arguments, which ($) is. Now try to guess the uncurry ($) type uncurry ($) . If type ($) is (a -> b) -> a -> b , replace it with (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 indicated above, which is the same. So what does this type tell us? uncurry ($) accepts a tuple (function, value) . Now try to guess what it does only with type.

Now, before answering, interlude. Haskell is so strongly typed that it prohibits returning a value of a particular type if the type declaration has a type variable as the return value of the type. Therefore, if you have a function of type a -> b , you cannot return String . This makes sense because if your function type was a -> a and you always returned String , how could the user pass a value to any other type? You must either be of type String -> String , or of type a -> a and return a value that depends solely on the input variable. But this limitation also means that it is not possible to write a function for certain types. There is no function with type a -> b , because no one knows what specific type should be instead of b . Or [a] -> a , do you know that this function cannot be total , since the user can pass an empty list and what will the function return in this case? The type a must depend on the type inside the list, but the list does not contain "inside", it is empty, so you do not know what the type of elements is inside the empty list. This restriction allows only for a very narrow elbow room for possible functions under a certain type, and that is why you get so much information about the possible behavior of a function by simply reading the type.

uncurry ($) returns something of type c , but it is a type variable, not a specific type, so its value depends on what is also of type c . And we see from the type declaration that the function in the tuple returns values ​​of type c . And the same function requests a value of type b , which can be found in only one tuple. There are no concrete types and types, so the only thing that uncurry ($) can do is take the snd tuple, put it as an argument into the function in the fst tuple, return everything that it returns:

 uncurry ($) ((+2), 2) -- 4 uncurry ($) (head, [1,2,3]) -- 1 uncurry ($) (map (+1), [1,2,3]) -- [2,3,4] 

There is a nice djinn program that generates type-based Haskell programs. Play with him to see that our uncurry ($) assumptions are true:

 Djinn> f ? a -> a f :: a -> a fa = a Djinn> f ? a -> b -- f cannot be realized. Djinn> f ? (b -> c, b) -> c f :: (b -> c, b) -> c f (a, b) = ab 

It also shows that fst and snd are the only functions that can have corresponding 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 
+2
Feb 19 '15 at 21:58
source share



All Articles