6. Safe pattern matching with >:>

Suppose we have the following List EADT:

data ConsF a l = ConsF a l deriving (Functor)
data NilF    l = NilF      deriving (Functor)

eadtPattern 'ConsF "Cons"
eadtPattern 'NilF  "Nil"

type List a = EADT '[ConsF a, NilF]

-- pattern for a specific EADT: List a
pattern ConsList :: a -> List a -> List a
pattern ConsList a l = Cons a l

Using classic pattern matching on List constructors as we do below isn’t really typesafe because the compiler cannot detect that the pattern matching is complete, hence we have the choice between a warning or adding a wildcard match:

showEADTList :: Show a => List a -> String
showEADTList = \case
   ConsList a l -> show a ++ " : " ++ showEADTList l
   Nil          -> "Nil"
   _            -> undefined -- this line avoids the warning but is unsafe
                             -- if we add constructors in the future

A safe alternative is to rely on multi-continuations: we can transform any EADT '[A,B,C] into a function whose type is (A -> r, B -> r, C -> r) -> r with the (>:>) operator. Then we can safely provide a function per constructor as in a pattern-matching.

Explicit recursion example

import Haskus.Utils.ContFlow

showCont' l = l >:>
   ( \(ConsF a r) -> show a ++ " : " ++ showCont' r -- explicit recursion
   , \NilF        -> "Nil"
   )

> showCont' intList
"10 : 20 : 30 : Nil"

Recursion schemes (e.g. catamorphism)

showCont l = l >:>
   ( \(ConsF a r) -> show a ++ " : " ++ r -- no explicit recursion
   , \NilF        -> "Nil"
   )

> cata showCont intList
"10 : 20 : 30 : Nil"

See recursion schemes.