packages feed

compdata-param-0.8: examples/Examples/Lambda.hs

{-# LANGUAGE TemplateHaskell, TypeOperators, MultiParamTypeClasses,
  FlexibleInstances, FlexibleContexts, UndecidableInstances,
  OverlappingInstances, Rank2Types, GADTs #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  Examples.Param.Lambda
-- Copyright   :  (c) 2011 Patrick Bahr, Tom Hvitved
-- License     :  BSD3
-- Maintainer  :  Tom Hvitved <hvitved@diku.dk>
-- Stability   :  experimental
-- Portability :  non-portable (GHC Extensions)
--
-- Lambda calculus examples
--
-- We define a pretty printer, a desugaring transformation, constant folding,
-- and call-by-value interpreter for an extended variant of the simply typed
-- lambda calculus.
--
--------------------------------------------------------------------------------

module Examples.Lambda where

import Data.Comp.Param
import Data.Comp.Param.Show ()
import Data.Comp.Param.Equality ()
import Data.Comp.Param.Ordering ()
import Data.Comp.Param.Derive
import Data.Comp.Param.Desugar

data Lam a b   = Lam (a -> b)
data App a b   = App b b
data Const a b = Const Int
data Plus a b  = Plus b b
data Let a b   = Let b (a -> b)
data Err a b   = Err

type Sig       = Lam :+: App :+: Const :+: Plus :+: Let :+: Err
type Sig'      = Lam :+: App :+: Const :+: Plus :+: Err

$(derive [smartConstructors, makeDifunctor, makeShowD, makeEqD, makeOrdD]
         [''Lam, ''App, ''Const, ''Plus, ''Let, ''Err])

-- * Pretty printing
data Stream a = Cons a (Stream a)

class Pretty f where
  prettyAlg :: Alg f (Stream String -> String)

$(derive [liftSum] [''Pretty])

pretty :: (Difunctor f, Pretty f) => Term f -> String
pretty t = cata prettyAlg t (nominals 1)
    where nominals n = Cons ('x' : show n) (nominals (n + 1))

instance Pretty Lam where
  prettyAlg (Lam f) (Cons x xs) = "(\\" ++ x ++ ". " ++ f (const x) xs ++ ")"

instance Pretty App where
  prettyAlg (App e1 e2) xs = "(" ++ e1 xs ++ " " ++ e2 xs ++ ")"

instance Pretty Const where
  prettyAlg (Const n) _ = show n

instance Pretty Plus where
  prettyAlg (Plus e1 e2) xs = "(" ++ e1 xs ++ " + " ++ e2 xs ++ ")"

instance Pretty Err where
  prettyAlg Err _ = "error"

instance Pretty Let where
  prettyAlg (Let e1 e2) (Cons x xs) = "let " ++ x ++ " = " ++ e1 xs ++ " in " ++ e2 (const x) xs

-- * Desugaring
instance (Difunctor f, App :<: f, Lam :<: f) => Desugar Let f where
  desugHom' (Let e1 e2) = inject (Lam e2) `iApp` e1

-- * Constant folding
class Constf f g where
  constfAlg :: forall a. Alg f (Trm g a)

$(derive [liftSum] [''Constf])

constf :: (Difunctor f, Constf f g) => Term f -> Term g
constf t = Term (cata constfAlg t)

instance (Difunctor f, f :<: g) => Constf f g where
  constfAlg = inject . dimap Var id -- default instance

instance (Plus :<: f, Const :<: f) => Constf Plus f where
  constfAlg (Plus e1 e2) = case (project e1, project e2) of
                             (Just (Const n),Just (Const m)) -> iConst (n + m)
                             _                               -> e1 `iPlus` e2

-- * Call-by-value evaluation
data Sem m = Fun (Sem m -> m (Sem m)) | Int Int

class Monad m => Eval f m where
  evalAlg :: Alg f (m (Sem m))

$(derive [liftSum] [''Eval])

eval :: (Difunctor f, Eval f m) => Term f -> m (Sem m)
eval = cata evalAlg

instance Monad m => Eval Lam m where
  evalAlg (Lam f) = return (Fun (f . return))

instance Monad m => Eval App m where
  evalAlg (App mx my) = do x <- mx
                           case x of Fun f -> f =<< my; _ -> fail "stuck"

instance Monad m => Eval Const m where
  evalAlg (Const n) = return (Int n)

instance Monad m => Eval Plus m where
  evalAlg (Plus mx my) = do x <- mx
                            y <- my
                            case (x,y) of (Int n,Int m) -> return (Int (n + m))
                                          _             -> fail "stuck"

instance Monad m => Eval Err m where
  evalAlg Err = fail "error"

e :: Term Sig
e = Term (iLet (iConst 2) (\x -> (iLam (\y -> y `iPlus` x) `iApp` iConst 3)))

e' :: Term Sig'
e' = desugar e

evalEx :: Maybe (Sem Maybe)
evalEx = eval e'