haskell 中不同元组的列表

Lists of different tuples in haskell

姬,在!

我想制作一个包含元组的列表,这样的元组由一个字符串组成,第二个元素是一个整数或布尔值

我找到了这个答案:

但我认为这有点不同,因为我必须使用已经定义的数据,而不是使用 Int 或 Bool,问题是我不能将 Num(通过 Int)或 B(通过 Bool)返回。

我的代码

data EAB = Var String
        |  Num Int
        |  B Bool   
        |  Sum EAB EAB
        |  Prod EAB EAB
        |  Neg EAB
        |  Pred EAB
        |  Suc EAB
        |  And EAB EAB
        |  Or EAB EAB
        |  Not EAB
        |  Iszero EAB
        |  If EAB EAB EAB
        |  Let EAB EAB
        |  Abs String EAB 
-- Arithmetic and Boolean expression language

-- Example that I want
-- Ctx = [("x",Num),("y", B),("z", B),("w",Num)] 

-- I tried 
-- data Type = Num EAB | B EAB
-- type Ctx = [(String, Type)]
-- but doesn't work

这是因为我想定义一个函数,告诉我是否可以根据我的 EAB 对等类型进行评估,例如

vt :: Ctx → EAB → Type → Bool
--- ...

-- Results
vt [] 4+5 Num = True
vt [] 4+False Num = False
vt [(x,Num)] 4+x Num = True

谢谢!

只需创建一个新的类型,使用新的构造函数。

data Type = NumT | BT

ctx = [("x", NumT), ("y", BT), ("z", BT), ("w", NumT)]

vt :: Ctx -> EAB -> Type -> Bool
vt [] (Sum (Num 4) (Num 5)) NumT = True -- etc.

您无法创建您想要的列表,但即使您可以,您也无法创建您的 vt 功能。

您似乎打算使用 EAB 类型的 BNum 构造函数来表示 EAB 语言中的类型。那是不可能的。

构造函数在 Haskell 中很特殊,因为您可以在模式匹配中使用它们。您可以编写一个模式,在其中将构造函数完全应用于其他模式,然后检查它是否与模式类型的值匹配。像这样:

case expr :: EAB
  of Var s -> "Var: " <> s
     Num n -> "Num: " <> show n
     B b -> "B: " <> show b

除此之外,构造函数没什么特别的;它们只是函数1。所以你的 NumInt -> EAB 类型的函数,你的 BBool -> EAB 类型的函数。您不能 以任何方式比较函数。你唯一能用函数做的就是应用它(到 correctly-typed 参数)然后看看你得到什么。

这意味着你想象的vt是不可能的。在第三个参数中(你说应该是你没有定义的 Type 类型),你使用了 Num。但请记住,Num 只是 Int -> EAB 类型的函数。 vt 函数实现无法查看它接收的函数并测试它是否为 Num 以帮助决定 return 值是否应为 TrueFalse。它唯一可能做的就是将它应用于 Int,然后检查 EAB 值。

但是你大概希望能够在那个位置通过B,这样vt可以说给定的EAB 表达式可以计算为布尔值。 B 是一个不同类型的函数 Bool -> EABvt 的第三个参数接受任何一种函数的唯一方法是用类型变量泛化它,使这个参数成为 (a -> EAB)。但是 vt 没有办法知道这个函数可以应用到什么地方,所以它甚至不能做你 可以 用函数做的一件事!2

基本上 NumB 不能用作您的语言中类型的抽象表示。它们是 EAB 数据类型的构造函数,可帮助您使用您的语言构建和检查值。这就是他们的全部目的。因此,真的没有必要进一步研究为什么你不能在(内部的元组)列表 3 中混合 NumB 或者你可能是怎样的能够解决它。

Daniel Wagner 的建议只是停止尝试为此目的滥用 EAB 的构造函数,只是 制作一个数据类型来表示您的语言中的类型! 这无论如何要好得多,因为 EAB 有 15 个构造函数,但您的语言似乎只有两种类型。4

所以你只需这样做:

data EABType = NumT | BT
  deriving (Eq, Show)

type Ctx = [(String, EABType)]
-- e.g. [("x",NumT),("y", BT),("z", BT),("w",NumT)

vt :: Ctx -> EAB -> EABType -> Bool
vt ctx expr ty
  = case expr of
      -- making some guesses about how you want vt to behave
      Var s -> (s, ty) `elem` ctx
      B _ -> ty == BT
      Num _ -> ty == NumT
      Sum l r -> ty == NumT && vt ctx l BT && vt ctx r BT
      And l r -> ty == BT && vt ctx l BT && vt ctx r BT
      ...

1 除非构造函数没有参数,否则它不是函数。你仍然无法比较它们,除非通过模式匹配(Eq 实例通过模式匹配简单地实现)。但是 nullary 构造函数本身就是一个完整的模式,因此不存在您无法比较的未应用的 nullary 构造函数。


2 假设我们可以对变量使用类型 class 约束,这可能使调用函数成为可能。例如如果它是 (Monoid a => a -> EAB),那么我们总是可以用 mempty 调用它。但是,您无法对这个想法做任何实际帮助您将此参数用于您预期目的的事情,所以我不会在这个方向上走得更远。


3 但要强调重点:原因与之前相同。 BNum 在你使用的上下文中它们只是具有不同类型的函数,你不能在列表中混合不同类型的函数,即使你可以你也不能 一旦他们在列表中就对他们做任何事情(比如查看列表中的每个元组并检查那里有哪个函数)。


4 假设您要使用简单的值类型,而不是尝试将 Bool -> Bool -> Bool 之类的类型分配给未应用的函数。