这个A-normal表单的实现是否与AST的大小成线性关系?

时间:2014-09-11 17:05:46

标签: haskell

The Essence of Compiling with Continuations Flanagan等人。描述了将术语转换为A-normal form的线性时间算法。简而言之,A-normal表单只允许所有应用程序使用琐碎的参数(例如变量),并且所有非平凡的术语都被绑定。

这是A-normal形式的一个(愚蠢的)例子:

let x0 = x0 in
let x2 = \ x1 -> x1 in
let x3 = 1 in
x2 x3

从这个非A-normal形式的术语创建:

let y = y in
(\ x0 -> x0) 1

我已将本文末尾给出的Scheme代码(第11页)翻译成下面的Haskell模块。但是,我介绍了monads给我一个新名称的来源,并保留重命名变量的映射。我的问题是,是否使用monads破坏了论文中算法的O(n)运行时间?我特别担心>>=的嵌套,这对于一些monad会导致O(n ^ 2)行为。

-- Based on http://slang.soe.ucsc.edu/cormac/papers/pldi93.pdf
import Control.Applicative
import Control.Monad.State.Strict
import Control.Monad.Reader
import qualified Data.Map.Strict as M

type Name = String
data Term = Var Name
          | Lam Name Term
          | App Term Term
          | Let Name Term Term
          | Lit Int
           deriving Show

type NameSupply a = ReaderT (M.Map Name Name) (State [Name]) a

fresh :: NameSupply String
fresh = do
    (name:rest) <- get
    put rest
    return name

normalizeTerm :: Term -> NameSupply Term
normalizeTerm m = normalize m return

normalize :: Term -> (Term -> NameSupply Term) -> NameSupply Term
normalize m k = case m of
    Var x       -> do
        mx' <- asks (M.lookup x)
        case mx' of
            Just x' -> k (Var x')
            Nothing -> error $ "var not found: " ++ x
    Lam x body  -> do
        x' <- fresh
        k =<< (Lam x' <$> local (M.insert x x') (normalizeTerm body))
    Let x m1 m2 -> do
        x' <- fresh
        local (M.insert x x') $ normalize m1 (\ n1 -> Let x' n1 <$>  (normalize m2 k))
    App m1 m2   -> normalizeName m1 (\ n1 -> normalizeName m2 (k . App n1))
    (Lit _)     -> k m

normalizeName :: Term -> (Term -> NameSupply Term) -> NameSupply Term
normalizeName m k = normalize m $ \ n -> do
    x <- fresh
    Let x n <$> k (Var x)

run :: Term -> Term
run = flip evalState names . flip runReaderT M.empty . normalizeTerm
  where names = (map (("x" ++) . show) [0..])

example :: Term
example = Let "y" (Var "y") (App (Lam "x0" (Var "x0")) (Lit 1))

1 个答案:

答案 0 :(得分:2)

你的monad只有ReaderT ... (State ...)ReaderTState都会传递单个值,因此没有理由期望线性时间(>>=)或O( n)链取O(n ^ 2)时间。

当monad使用列表或集合或类似时,你可以得到左嵌套的二次爆破。