packages feed

syntactic-0.5: Examples/ALaCarte.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}

-- | Demonstration of the fact that "Language.Syntactic" has the same
-- functionality as /Data types à la carte/ (Wouter Swierstra,
-- /Journal of Functional Programming/, 2008)

module ALaCarte where



import Language.Syntactic



data Val a
  where
    Val :: Int -> Val (Full Int)

data Add a
  where
    Add :: Add (Int :-> Int :-> Full Int)

data Mul a
  where
    Mul :: Mul (Int :-> Int :-> Full Int)

instance Eval Val
  where
    evaluate (Val a) = Full a

instance Eval Add
  where
    evaluate Add = Partial $ \a -> Partial $ \b -> Full (a+b)

instance Eval Mul
  where
    evaluate Mul = Partial $ \a -> Partial $ \b -> Full (a*b)

instance Render Val
  where
    render (Val a) = show a

instance Render Add
  where
    render Add = "(+)"

instance Render Mul
  where
    render Mul = "(*)"



-- Manual injection:

addExample :: ASTF (Val :+: Add) Int
addExample = Symbol (InjectR Add) :$: Symbol (InjectL (Val 118)) :$: Symbol (InjectL (Val 1219))



-- Automatic injection:

val :: (Val :<: expr) => Int -> ASTF expr Int
val = inject . Val

(<+>) :: (Add :<: expr) => ASTF expr Int -> ASTF expr Int -> ASTF expr Int
a <+> b = inject Add :$: a :$: b

(<*>) :: (Mul :<: expr) => ASTF expr Int -> ASTF expr Int -> ASTF expr Int
a <*> b = inject Mul :$: a :$: b

infixl 6 <+>
infixl 7 <*>

example1 :: ASTF (Add :+: Val) Int
example1 = val 30000 <+> val 1330 <+> val 7

test1 = evaluate example1

example2 :: ASTF (Val :+: Add :+: Mul) Int
example2 = val 80 <*> val 5 <+> val 4

test2 = evaluate example2

example3 :: ASTF (Val :+: Mul) Int
example3 = val 6 <*> val 7

test3 = evaluate example3

example4 :: ASTF (Val :+: Add :+: Mul) Int
example4 = val 80 <*> val 5 <+> val 4

test4 = render example4



-- Pattern matching:

distr :: (Add :<: expr, Mul :<: expr, ConsType a) => AST expr a -> AST expr a
distr ((project -> Just Mul) :$: a :$: b) = case distr b of
    (project -> Just Add) :$: c :$: d -> a' <*> c <+> a' <*> d
    b' -> a' <*> b'
  where
    a' = distr a
distr (f :$: a) = distr f :$: distr a
distr a         = a
  -- Note the use of direct recursion instead of a fold combinator

example5 :: ASTF (Val :+: Add :+: Mul) Int
example5 = val 80 <*> (val 5 <+> val 4) <+> val 543

test5 = render (distr example5)

example6 :: ASTF (Mul :+: Add :+: Val) Int
example6 = val 444 <*> (val 80 <*> (val 5 <+> val 3 <*> val 4))

test6 = render (distr example6)