Home > Enterprise >  Unpacking tuples using "Either" in Haskell
Unpacking tuples using "Either" in Haskell

Time:10-14

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 bis 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 either a is applied to c or b is applied to c

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.

  • Related