8
votes

In Haskell, it is easy to write functions that act on or return tuples of things, e.g. the prelude function splitAt:

splitAt :: Int -> [a] -> ([a], [a]) 

but is there no easy, convenient, way of writing functions that act on or result in cotuples of things? E.g. a function that returns an Int or a Double. As a concrete example, let's say I want to write a function

MyDivision :: Int -> Int -> (Int + Double)

where + is my symbol for cotupling, so MyDivision x y returns x/y as an Int if the division results in an integer but as a Double if the division does not result in an integer.

So far, it seems that I have two choices, either declare a new datatype

data IntOrDouble = AnInt Int | ADouble Double

or use

Either Int Double

where the first alternative requires a lot of typing and thinking of names and the second alternative quickly gets messy when you have larger cotuples and get types looking like

Either (Either a (Either b c)) (Either (Either d f) g)

Now, if I had a a cotuple type, say

a + b + c + d

I would like to be able to form functions

f :: (a + b + c + d) -> e
g :: (a + b + c + d) -> (e + f + g + h)

by just supplying functions

f1 :: a -> e, f2 :: b -> e, f3 :: c -> e, f4 :: d -> e
g1 :: a -> e, g2 :: b -> f, g3 :: c -> g, g4 :: d -> h

and setting

f = f1 + f2 + f3 + f4
g = g1 <+> g2 <+> g3 <+> g4

or something of the like.

Is this possible?

2
Are you aware of Either Int Double? - gspr
@gspr, yes I am. But until I saw jozefgs answer I thought it would be kinda messy to use it for larger cotuples. - Calle
Sorry, I now see you explicitly mention Either. I was on my phone and didn't see it. - gspr
That's alright, it was nice of you to show interest in my question. - Calle

2 Answers

13
votes

Well co-tuples are properly called coproducts which is just Either.

So, let's go ahead and do something like

{-# LANGUAGE TypeOperators #-}

type (+) = Either

This is left associative by the way. Now we have pretty syntax like

foo :: Int + Bool + Char
foo = Right 'c'

Now, what you seem to want there is in fact very similar to a church representation of Either flattened out. We can just build this up with the either combinator

(+) :: (a -> c)  -> (b -> c)  -> (a + b) -> c
l + r = either l r

(<+>) :: (a -> c) -> (b -> d) -> (a + b) -> (c + d)
l <+> r = either (Left . l) (Right . r)

infixl 4 <+>, +

A fun challenge would be to create a generic inject function which takes something like Proxy k where k is some representation of natural numbers at the type level and returns a great nested mess of Eithers for you.

Update:

I got bored, here's the code for generic inj

data Nat = S Nat | Z

type NatRep (n :: Nat) = Proxy n

type family Tuplish (l :: Nat) (r :: Nat) t
type instance Tuplish Z Z t     = t
type instance Tuplish (S n) Z t = (Tuplish n Z ()) + t
type instance Tuplish l (S n) t = (Tuplish l n t)  + ()

predP :: Proxy (S n) -> Proxy n
predP = reproxy

class Inject (l :: Nat) (r :: Nat) v where
  inj :: NatRep l -> NatRep r -> v  -> Tuplish l r v
instance Inject Z Z v where
  inj _ _ = id
instance Inject (S n) Z v where
  inj _ _ v = Right v
instance Inject n m v => Inject n (S m) v where
  inj l r v = Left (inj l (predP r) v)
6
votes

I renamed your + to >+< and your <+> to >*<, but you could do something like this:

type a + b = Either a b

(>+<) :: (a -> c) -> (b -> c) -> a + b -> c
(>+<) = either

(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(f >*< _) (Left  a) = Left  (f a)
(_ >*< g) (Right b) = Right (g b)

I tried to name the operators to be more suggestive of their operation.

Here's another way to implement >*<:

import Control.Arrow ((+++))

(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(>*<) = (+++)

As a side note: "Tuples" are often called product types and this is what's called a coproduct type (or sum type). The most basic coproduct type is Either and all other coproduct types are isomorphic to Either A B for some types A and B.