(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