首页 > 解决方案 > 如何使用 GADT 和 Data.Map 创建类型化的玩具语言环境映射?

问题描述

我在玩 Haskell,想法是在 GADT 的帮助下创建一种玩具语言。解释器通常具有将变量名称映射到值的环境映射。

我的玩具语言 int 和 bool 基本上有两种类型,例如,用I 10and表示B True

问题是我无法创建包含这两者的地图,这是我的代码

{-# LANGUAGE GADTs, RankNTypes #-}

module Foo where

import qualified Data.Map as M
import Data.Maybe

data Expr a where
  V :: Char -> Expr a
  B :: Bool -> Expr Bool
  I :: Int -> Expr Int
  If :: Expr Bool -> Expr a -> Expr a -> Expr a

type Env a = M.Map Char (Expr a)

env = M.fromList [('x', B True), ('y', I 10)]
-- Error here                      ----^^^^
-- [typecheck -Wdeferred-type-errors] [E] • Couldn't match type ‘Int’ with ‘Bool’
--   Expected type: Expr Bool
--     Actual type: Expr Int
-- • In the expression: I 10
--   In the expression: ('y', I 10)
--   In the first argument of ‘M.fromList’, namely
--     ‘[('x', B True), ('y', I 10)]’

我怎样才能创建这样的映射?

标签: haskell

解决方案


对于这么小的类型集合,我会使用其中一个:

type Env = Map Variable (Either (Expr Int) (Expr Bool))
type Env = (Map Variable (Expr Int), Map Variable (Expr Bool))

...取决于您是否想要为每种类型使用单独的命名空间。当有更多类型可用时,我会使用其中一种:

data UntypedExpr = forall a. UE (TypeRep a) (Expr a)
type Env = Map Variable UntypedExpr
-- OR
newtype SingleTypeEnv a = STE (Map Variable (Expr a))
type Env = TypeRepMap SingleTypeEnv

...再次取决于您是否想要为每种类型使用单独的命名空间。

TypeRep来自基本包(或者您可以制作一些更特定于您的 EDSL 类型的新 GADT),并且TypeRepMap来自typerep-map


推荐阅读