Skip to content

Instantly share code, notes, and snippets.

@siraben
Last active December 2, 2020 16:01
Show Gist options
  • Save siraben/447c419508b460afc99a232d5df8063f to your computer and use it in GitHub Desktop.
Save siraben/447c419508b460afc99a232d5df8063f to your computer and use it in GitHub Desktop.
A-Normalization algorithm in Haskell
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
-- A-Normalization, based on Matt Might's blog post:
-- http://matt.might.net/articles/a-normalization/
import Control.Monad.Cont
import Control.Monad.State
import Data.Function
data Value
= N Int
| S String
| V Var
| B Bool
deriving (Show, Eq)
type Var = String
data Exp
= Val Value
| L [Var] Exp
| App [Exp]
| If Exp Exp Exp
| Let (Var, Exp) Exp
| Set Var Exp
deriving (Show, Eq)
type Prog = [DecIn]
data DecIn
= DefFunc Var [String] Exp
| DefVar Var Exp
| InExp Exp
deriving (Show, Eq)
data DecOut
= Define Var Exp
| Begin [DecOut]
| OutExp Exp
deriving (Show, Eq)
newtype ANormal r a =
ANormal
{ runANormal :: StateT Int (Cont r) a }
deriving (Functor, Applicative, Monad, MonadState Int, MonadCont)
norm :: ANormal b b -> b
norm x = x
& runANormal
& (`evalStateT` 0)
& (`runCont` id)
gensym :: ANormal r String
gensym = do
n <- get
modify (+ 1)
pure ('t' : show n)
normalize :: Exp -> ANormal r Exp
normalize (L ps b) = L ps <$> normalize b
normalize (Let (x, m1) m2) = Let . (x, ) <$> normalize m1 <*> normalize m2
normalize (If m1 m2 m3) = do
runCont (normalizeName' m1) (\n1 -> If n1 <$> normalize m2 <*> normalize m3)
normalize (App l) = runCont (normalizeNames' l) (pure . App)
normalize (Set v e) = runCont (normalizeName' e) f
where
f t = do
x <- gensym
pure $ Let (x, Set v t) (Val (S "void"))
normalize (Val v) = pure $ Val v
normalizeDefine :: DecIn -> ANormal r DecOut
normalizeDefine (DefFunc f params body) = Define f <$> normalize (L params body)
normalizeDefine (DefVar v exp) = Begin . (`flattenTop` v) <$> normalize exp
normalizeDefine (InExp x) = pure (OutExp x)
normalizeName' :: Exp -> Cont (ANormal r Exp) Exp
normalizeName' m =
cont $ \k -> do
n <- normalize m
case n of
Val _ -> k n
_ -> do
t <- gensym
Let (t, n) <$> k (Val (S t))
flattenTop :: Exp -> String -> [DecOut]
flattenTop (Let (x, e1) e2) v = Define x e1 : flattenTop e2 v
flattenTop exp v = [Define v exp]
normalizeNames' :: [Exp] -> Cont (ANormal r Exp) [Exp]
normalizeNames' = mapM normalizeName'
normalizeProg :: [DecIn] -> ANormal r [DecOut]
normalizeProg = mapM g
where
g x@(DefVar _ _) = normalizeDefine x
g x@(DefFunc _ _ _) = normalizeDefine x
g (InExp x) = OutExp <$> normalize x
var :: String -> Exp
var = Val . S
val :: Int -> Exp
val = Val . N
ex1 :: Exp
ex1 =
App [ App [var "f", var "g"]
, App [var "h", var "x"]
, val 3]
ex2 :: Prog
ex2 =
[DefFunc "f" ["n"]
(If (App [var "=", var "n", val 0])
(val 1)
(App [var "*", var "n",
App [var "f", App [var "-", var "n", val 1]]]))]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment