mysterious-incomposability-of-decidable

The Mysterious Incomposability of Decidable

Applicative , Alternative and Divisible are Haskell classes that each have nice composition properties. There is a fourth class, Decidable , that fills in the remaining corner of a square of properties but I cannot find any nice composition property for it.

By way of introduction, Haskell has a Functor class that can be fmap ped covariantly and a Contravariant class that can be contramap ped contravariantly. Subclasses of these allow pairs of the same type to be combined. In brief

Applicative : covariant, converts products to products

: covariant, converts products to products Alternative : covariant, converts products to sums

: covariant, converts products to sums Divisible : contravariant, converts products to products

: contravariant, converts products to products Decidable : contravariant, converts products to sums

The following code demonstrates those properties:

import Control.Applicative import Data.Functor.Contravariant import Data.Functor.Contravariant.Divisible applicativeProduct :: Applicative f => (f a, f b) (f a, f b) -> f (a, b) f (a, b) = (,) <$> fa <*> fb applicativeProduct (fa, fb)(,)fafb alternativeSum :: Alternative f => (f a, f b) (f a, f b) -> f ( Either a b) f (a b) = fmap Left fa <|> fmap Right fb alternativeSum (fa, fb)fafb divisibleProduct :: Divisible f => (f a, f b) (f a, f b) -> f (a, b) f (a, b) = divide id fa fb divisibleProduct (fa, fb)dividefa fb decidableProduct :: Decidable f => (f a, f b) (f a, f b) -> f ( Either a b) f (a b) = chosen fa fb decidableProduct (fa, fb)chosen fa fb

Applicative s, Alternative s and Divisible s compose well. The first with <$> and <*> and the second and third with fmap / contramap and a monoidal operation ( <|> and what I define as <+> respectively).

applicativeCompose :: [[ String ]] [[]] = f <$> [ 1 , 2 ] applicativeCompose <*> [ True , False ] <*> [ "hello" , "world" ] where f = (\a b c -> replicate a ( if b then c else "False" )) (\a b ca ()) alternativeCompose :: [ String ] = fmap show [ 1 , 2 ] alternativeCompose <|> fmap reverse [ "hello" , "world" ] divisibleCompose :: Predicate ( String , Int ) = contramap (( == 5 ) . length . fst ) predicate divisibleComposecontramap (() predicate <+> contramap (( < 6 ) . snd ) predicate contramap (() predicate

However, I cannot work out any nice way to compose Decidable s and I find this very mysterious. For example, suppose I have

instance F Decidable a :: F A b :: F B c :: F C data Foo = Bar A | Baz B | Quux C

How do I compose a , b and c into an F Foo ? The simplest thing I have discovered is

compose :: F Foo = f -$- (fC -*- fB -*- fA) compose(fCfBfA) where f = \ case Bar a -> Right a Baz b -> Left ( Right b) b) Quux c -> Left ( Left c) c)

with the Decidable operations -$- and -*- given by

(-$-) :: Contravariant f => (a -> b) -> f b -> f a (ab)f bf a ( -$- ) = contramap contramap (-*-) :: Decidable f => f a -> f b -> f ( Either a b) f af bf (a b) ( -*- ) = chosen chosen infixl -*- infixr -$-

The explicit unpacking into an Either is rather unsatisfactory. I’ve tried all sorts of techniques, including CPS, but nothing seems to make Decidable s nicely composable. Do you have any ideas? If so, contact me!

Appendix

Some extra code used in the examples: