I'm trying to get a grip on how tuples work in Haskell.
I came across this type constructor leftRight :: (Either a b -> c) -> (a -> c, b -> c)
and I'm struggling to see what it does.
So we have (Either a b -> c)
which means that either a is applied to c or b is applied to c. But the output is a tuple?
Does that mean that it splits the function so that left side of the tuple takes in argument given at a and right side takes argument given at right?
How would this look in code?
CodePudding user response:
"I came across this type constructor..."
It's not a type constructor - it's a function type declaration.
The ->
separates out the parameter types. The final one is the return type, and the previous ones are the input types.
Hence leftRight :: (Either a b -> c) -> (a -> c, b -> c)
takes one input and returns one output.
- Input function:
(Either a b -> c)
- Output function pair:
(a -> c, b -> c)
The parentheses contain the functions.
The first function takes an Either type (left value is the error type, a
, and the right value is the OK type, 'b' - it helps me to think of the latin, sinister for left, and dexter for right - your mileage may vary) as the input and returns something of type c
.
The second function comes as a tuple of two separate functions, one is a -> c
, and one is b -> c
.
A concrete version: type a
is a String to contain my error message, type b
is an Int, and type c
is another string.
leftRight :: (Either String Int -> String) -> (String -> String, Int -> String)
CodePudding user response:
The input to leftRight
is a function and the output is two functions. The tuple is barely relevant, except that it groups the two functions together into a single output, so leftRight
can return both of them at once.
One function's type is a -> c
and the other one's type is b -> c
. Presumably, the first function wraps the a
in Left
and then calls the original function, and the second one wraps the b
in Right
and then calls the original function:
leftRight :: (Either a b -> c) -> (a -> c, b -> c)
leftRight original = (leftFunc, rightFunc)
where
leftFunc aVal = original (Left aVal)
rightFunc bVal = original (Right bVal)
CodePudding user response:
So we have
Either a b -> c
which means that eithera
is applied toc
orb
is applied toc
Wrong, or at least badly worded. Nothing is “applied to c
” here.
What it actually means is: the function can accept an a
-value or a b
-value, and in either case produces a c
-value.
This is equivalent to having both a function that accepts only a
-values and gives c
-results, and one that accepts only b
-values and gives c
-results. The tuple groups both of these functions together.
It might help to look at an example to illustrate:
fryPancake :: Either Butter Margarine -> Pancake
[Assume we've already taken care of the flour, eggs etc. in the batter]
From this you can obtain both
fryPancakeWithButter :: Butter -> Pancake
fryPancakeWithButter b = fryPancake (Left b)
fryPancakeWithMargarine :: Margarine -> Pancake
fryPancakeWithMargarine m = fryPancake (Right m)
Now you just group both of these together:
fryPancake' :: (Butter -> Pancake, Margarine -> Pancake)
fryPancake' = (fryPancakeWithButter, fryPancakeWithMargarine)
...which is the same as
fryPancake' = leftRight fryPancake
CodePudding user response:
The key is to use function composition. A function of type a -> c
can be created from functions of type a -> Either a b
and Either a b -> c
. You have Left :: a -> Either a b
and you have the argument f :: Either a b -> c
passed to leftRight
.
The same argument lets you construct a function of type b -> c
similarly. This gives you
leftRight :: (Either a b -> c) -> (a -> c, b -> c)
leftRight f = let g1 = ...
g2 = ...
in (g1, g2)
I leave the definitions of g1
and g2
as an exercise.