Ad-hoc cotuples in Haskell

In Haskell, it's easy to write functions that act or return tuples of things, for example. splitAt foreplay function:

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

but is there a simple, convenient way to write functions that act or lead to a totality of things? For example. function that returns an int or double. As a specific example, suppose I want to write a function

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

where + is my cotupling character, so MyDivision xy returns x / y as Int if division leads to an integer, but like Double if division does not lead to an integer.

So far it seems that I have two options: declare a new data type

 data IntOrDouble = AnInt Int | ADouble Double 

or use

 Either Int Double 

where the first alternative requires a lot of typing and thinking about names, and the second alternative quickly becomes messy when you have large nodes and get types similar to

 Either (Either a (Either bc)) (Either (Either df) g) 

Now, if I had a cotuple type, say

 a + b + c + d 

I would like to be able to create functions

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

just delivering 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 installation

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

or something similar.

Is it possible?

+8
haskell
source share
2 answers

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

 type a + b = Either ab (>+<) :: (a -> c) -> (b -> c) -> a + b -> c (>+<) = either (>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f (f >*< _) (Left a) = Left (fa) (_ >*< g) (Right b) = Right (gb) 

I tried to call the operators more suggestive of my work.

Here is another way to implement >*< :

 import Control.Arrow ((+++)) (>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f (>*<) = (+++) 

As a side note: β€œTuples” are often referred to as product types, and this is what is called a companion type type (or sum type). The most basic type of coprocess is Either , and for other types A and B all other types of coproducts are isomorphic to Either AB .

+6
source share

Code tuples are correctly called co-products, which are simply Either .

So let go and do something like

 {-# LANGUAGE TypeOperators #-} type (+) = Either 

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

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

Now, what you find desirable is actually very similar to the Either church smoothed view. We can just build it with the Either combinator

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

An interesting task would be to create a common inject function that takes something like Proxy k , where k is some representation of natural numbers at the type level and returns a big nested Either mess for you.

Update:

I got bored, here is the code for general inj

 data Nat = S Nat | Z type NatRep (n :: Nat) = Proxy n type family Tuplish (l :: Nat) (r :: Nat) t type instance Tuplish ZZ t = t type instance Tuplish (S n) Z t = (Tuplish n Z ()) + t type instance Tuplish l (S n) t = (Tuplish lnt) + () predP :: Proxy (S n) -> Proxy n predP = reproxy class Inject (l :: Nat) (r :: Nat) v where inj :: NatRep l -> NatRep r -> v -> Tuplish lrv instance Inject ZZ v where inj _ _ = id instance Inject (S n) Z v where inj _ _ v = Right v instance Inject nmv => Inject n (S m) v where inj lrv = Left (inj l (predP r) v) 
+13
source share

All Articles