代码之家  ›  专栏  ›  技术社区  ›  CMCDragonkai

Haskell组合语言解释器系统

  •  8
  • CMCDragonkai  · 技术社区  · 6 年前

    在前一个问题中 SystemT Compiler and dealing with Infinite Types in Haskell 我询问了如何将SystemT Lambda演算解析为SystemT组合子。我决定使用普通代数数据类型来编码SystemT Lambda演算和SystemT Combinator演算(基于注释: SystemT Compiler and dealing with Infinite Types in Haskell ).

    SystemTCombinator.hs:

    module SystemTCombinator where
    
    data THom = Id
              | Unit
              | Zero
              | Succ
              | Compose THom THom
              | Pair THom THom
              | Fst
              | Snd
              | Curry THom
              | Eval
              | Iter THom THom
              deriving (Show)
    

    {-# LANGUAGE ExistentialQuantification #-}
    {-# LANGUAGE FlexibleInstances         #-}
    {-# LANGUAGE MultiParamTypeClasses     #-}
    {-# LANGUAGE PartialTypeSignatures     #-}
    {-# LANGUAGE TypeSynonymInstances      #-}
    
    module SystemTLambda where
    
    import           Control.Monad.Catch
    import           Data.Either (fromRight)
    import qualified SystemTCombinator    as SystemTC
    
    type TVar = String
    
    data TType = One | Prod TType TType | Arrow TType TType | Nat deriving (Eq)
    
    instance Show TType where
      show ttype = case ttype of
        One -> "'Unit"
        Nat -> "'Nat"
        Prod ttype1 ttype2 ->
          "(" ++ show ttype1 ++ " * " ++ show ttype2 ++ ")"
        Arrow ttype1@(Arrow _ _) ttype2 ->
          "(" ++ show ttype1 ++ ") -> " ++ show ttype2
        Arrow ttype1 ttype2 -> show ttype1 ++ " -> " ++ show ttype2
    
    data TTerm = Var TVar
               | Let TVar TTerm TTerm
               | Lam TVar TTerm
               | App TTerm TTerm
               | Unit
               | Pair TTerm TTerm
               | Fst TTerm
               | Snd TTerm
               | Zero
               | Succ TTerm
               | Iter TTerm TTerm TVar TTerm
               | Annot TTerm TType
               deriving (Show)
    
    -- a context is a list of hypotheses/judgements
    type TContext = [(TVar, TType)]
    
    -- our exceptions for SystemT
    data TException = TypeCheckException String
                    | BindingException String
      deriving (Show)
    
    instance Exception TException
    
    newtype Parser a = Parser { run :: TContext -> Either SomeException a }
    
    instance Functor Parser where
      fmap f xs = Parser $ \ctx ->
        either Left (\v -> Right $ f v) $ run xs ctx
    
    instance Applicative Parser where
      pure a = Parser $ \ctx -> Right a
      fs <*> xs = Parser $ \ctx ->
        either Left (\f -> fmap f $ run xs ctx) (run fs ctx)
    
    instance Monad Parser where
      xs >>= f = Parser $ \ctx ->
        either Left (\v -> run (f v) ctx) $ run xs ctx
    
    instance MonadThrow Parser where
      throwM e = Parser (const $ Left $ toException e)
    
    instance MonadCatch Parser where
      catch p f = Parser $ \ctx ->
        either
          (\e -> case fromException e of
            Just e' -> run (f e') ctx -- this handles the exception
            Nothing -> Left e) -- this propagates it upwards
          Right
          $ run p ctx
    
    withHypothesis :: (TVar, TType) -> Parser a -> Parser a
    withHypothesis hyp cmd = Parser $ \ctx -> run cmd (hyp : ctx)
    
    tvarToHom :: TVar -> Parser (SystemTC.THom, TType)
    tvarToHom var = Parser $ \ctx ->
      case foldr transform Nothing ctx of
        Just x -> Right x
        Nothing -> throwM $ BindingException ("unbound variable " ++ show var)
      where
        transform (var', varType) homAndType =
          if var == var'
          then Just (SystemTC.Snd, varType)
          else homAndType >>= (\(varHom, varType) -> Just (SystemTC.Compose SystemTC.Fst varHom, varType))
    
    check :: TTerm -> TType -> Parser SystemTC.THom
    -- check a lambda
    check (Lam var bodyTerm) (Arrow varType bodyType) =
      withHypothesis (var, varType) $
      check bodyTerm bodyType >>= (\bodyHom -> return $ SystemTC.Curry bodyHom)
    check (Lam _    _    ) ttype                 = throwM
      $ TypeCheckException ("expected function type, got '" ++ show ttype ++ "'")
    -- check unit
    check Unit One = return SystemTC.Unit
    check Unit ttype =
      throwM $ TypeCheckException ("expected unit type, got '" ++ show ttype ++ "'")
    -- check products
    check (Pair term1 term2) (Prod ttype1 ttype2) = do
      hom1 <- check term1 ttype1
      hom2 <- check term2 ttype2
      return $ SystemTC.Pair hom1 hom2
    check (Pair _      _     ) ttype                = throwM
      $ TypeCheckException ("expected product type, got '" ++ show ttype ++ "'")
    -- check primitive recursion
    check (Iter baseTerm inductTerm tvar numTerm) ttype = do
      baseHom   <- check baseTerm ttype
      inductHom <- withHypothesis (tvar, ttype) (check inductTerm ttype)
      numHom    <- check numTerm Nat
      return $ SystemTC.Compose (SystemTC.Pair SystemTC.Id numHom)
                                (SystemTC.Iter baseHom inductHom)
    -- check let bindings
    check (Let var valueTerm exprTerm) exprType = do
      (valueHom, valueType) <- synth valueTerm
      exprHom <- withHypothesis (var, valueType) (check exprTerm exprType)
      return $ SystemTC.Compose (SystemTC.Pair SystemTC.Id valueHom) exprHom
    check tterm ttype = do
      (thom, ttype') <- synth tterm
      if ttype == ttype'
        then return thom
        else throwM $ TypeCheckException
          (  "expected type '"
          ++ show ttype
          ++ "', inferred type '"
          ++ show ttype'
          ++ "'"
          )
    
    synth :: TTerm -> Parser (SystemTC.THom, TType)
    synth (Var tvar) = tvarToHom tvar
    synth (Let var valueTerm exprTerm) = do
      (valueHom, valueType) <- synth valueTerm
      (exprHom, exprType) <- withHypothesis (var, valueType) (synth exprTerm)
      return (SystemTC.Compose (SystemTC.Pair SystemTC.Id valueHom) exprHom, exprType)
    synth (App functionTerm valueTerm) = do
      (functionHom, functionType) <- synth functionTerm
      case functionType of
        Arrow headType bodyType -> do
          valueHom <- check valueTerm headType
          return (SystemTC.Compose (SystemTC.Pair functionHom valueHom) SystemTC.Eval, bodyType)
        _ -> throwM $ TypeCheckException ("expected function, got '" ++ show functionType ++ "'")
    synth (Fst pairTerm) = do
      (pairHom, pairType) <- synth pairTerm
      case pairType of
        Prod fstType sndType -> return (SystemTC.Compose pairHom SystemTC.Fst, fstType)
        _ -> throwM $ TypeCheckException ("expected product, got '" ++ show pairType ++ "'")
    synth (Snd pairTerm) = do
      (pairHom, pairType) <- synth pairTerm
      case pairType of
        Prod fstType sndType -> return (SystemTC.Compose pairHom SystemTC.Snd, sndType)
        _ -> throwM $ TypeCheckException ("expected product, got '" ++ show pairType ++ "'")
    synth Zero = return (SystemTC.Compose SystemTC.Unit SystemTC.Zero, Nat)
    synth (Succ numTerm) = do
      numHom <- check numTerm Nat
      return (SystemTC.Compose numHom SystemTC.Succ, Nat)
    synth (Annot term ttype) = do
      hom <- check term ttype
      return (hom, ttype)
    synth _ = throwM $ TypeCheckException "unknown synthesis"
    

    SystemTLambda.TTerm 进入 SystemTCombinator.THom .

    systemTLambda :: TTerm
    systemTLambda =
      Let "sum"
        (Annot
          (Lam "x" $ Lam "y" $
           Iter (Var "y") (Succ $ Var "n") "n" (Var "x"))
          (Arrow Nat $ Arrow Nat Nat))
        (App
          (App
            (Var "sum")
            (Succ $ Succ Zero))
          (Succ $ Succ $ Succ Zero))
    
    systemTCombinator :: Either SomeException (SystemTC.THom, SystemTC.TType)
    systemTCombinator = fromRight Unit $ run (synth result) []
    

    Compose (Pair Id (Curry (Curry (Compose (Pair Id (Compose Fst Snd)) (Iter Snd (Compose Snd Succ)))))) (Compose (Pair (Compose (Pair Snd (Compose (Compose (Compose Unit Zero) Succ) Succ)) Eval) (Compose (Compose (Compose (Compose Unit Zero) Succ) Succ) Succ)) Eval)
    

    RuntimeException 因为组合表达式是非类型化的,所以可能存在错误的组合表达式。然而,我的类型检查器应该确保一旦到达解释器,组合符就应该已经很好地输入了。

    根据最初的博文, http://semantic-domain.blogspot.com/2012/12/total-functional-programming-in-partial.html

    evaluate Id = id
    evaluate Unit = const ()
    evaluate Zero = \() -> Z
    evaluate (Succ n) = S n
    evaluate (Compose f g) = (evaluate g) . (evaluate f)
    evaluate (Pair l r) = (evaluate l, evaluate r)
    evaluate Fst = fst
    evaluate Snd = snd
    evaluate (Curry h) = curry (evaluate h)
    evaluate Eval = \(f, v) -> f v
    evaluate (Iter base recurse) = \(a, n) ->
      case n of
        Z   -> evaluate base a
        S n -> evaluate recurse (a, evaluate (Iter base recurse) (a, n))
    

    THom 树,这样我可以得到某种函数,可以部分执行。

    2 回复  |  直到 6 年前
        1
  •  3
  •   luqui    6 年前

    THom 以保证类型良好的方式,您需要向Haskell类型检查器“解释”其类型。我看你已经考虑过GADT版本的 ,这将是做这个解释的常规方法,这仍然是我将采用的方法。如果我理解正确的话,你面临的问题是 synth 太复杂了,无法证明它总能生成一个类型良好的

    我想你可以保持沉默 (rougly)实际上,如果您添加一个简单的传递,该类型将检查结果的非类型化 汤姆 StrongTHom a b

    checkTHom :: THom -> TType a -> TType b -> Maybe (StrongTHom a b)
    

    (在哪里 TType 单件形式在 previous answer ). 它只要求您在顶层了解您的输入和输出类型。这通常是好的,因为为了实际使用结果,您最终必须知道实例化结果的类型(您可能必须将此预期类型信息向外推几层,直到知道具体类型为止)

    typeEq 下面),以及非类型化的 汤姆 可能还需要包含更多类型信息。

    汤姆 Compose :: THom a b -> THom b c -> THom a c b 被擦除和 checkTHom Compose 需要包含足够的信息,以便实现这一点。在这一点上,一个存在主义( SomeType 根据上一个答案)可能会很好,因为使用它的唯一方法是将其展开并递归传递。

    typeEq :: TType a -> TType b -> Maybe (a :~: b)
    

    :~: standard type equality )写起来容易;我只是想让你知道这个技巧。

    一旦你有了这个,那么 eval :: StrongTHom a b -> a -> b 应该像热黄油一样通过。祝你好运

        2
  •  2
  •   luqui    6 年前

    data Value
        = VUnit                          -- of type One
        | VPair Value Value              -- of type Pair
        | VFunc (Value -> Interp Value)  -- of type Func
        | VNat Integer                   -- of type Nat
    

    然后您可以非常直接地使用非类型化 THom ,以获得合适的解释器monad Interp (也许只是一个 Except 单子):

    eval :: THom -> Value -> Interp Value
    eval Id v  = v
    eval Unit _ = VUnit
    eval Zero VUnit = VNat Zero
    eval Succ (VNat n) = VNat (n + 1)
    ...
    eval _ _ = throwE "type error"
    

    还要注意 VFunc eval

    推荐文章