首页 > 解决方案 > 使用存在类型 Haskell 时出现类型错误

问题描述

我正在 Haskell 中对存在类型进行实验。我正在构建一个非常短的 Json 库并尝试构建一个折叠。我在使用forall量词的代码中出现错误!

class (Show m) => JsonValue m

instance JsonValue Int
instance JsonValue Double
instance JsonValue String
instance JsonValue Bool

data Json = JObject [(String, Json)]
          | JArray [Json]
          | forall a . (JsonValue a) => JValue a

foldJ :: ([(String, b)] -> b) -> ([b] -> b) -> forall a . (JsonValue a) => (a -> b) -> Json -> b
foldJ object array value (JObject xs) = object $ map (bimap id (foldJ object array value)) xs
foldJ object array value (JArray xs)  = array $ map (foldJ object array value) xs
foldJ object array value (JValue x)   = value x -- ERROR HERE

我在用-XExistentialTypes -XFlexibleInstances -XRank2Types

错误如下所示:

Json.hs:335:47: error:
    • Couldn't match expected type ‘a’ with actual type ‘a1’
      ‘a1’ is a rigid type variable bound by
        a pattern with constructor:
          JValue :: forall a. JsonValue a => a -> Json,
        in an equation for ‘foldJ’
        at Json.hs:335:27-34
      ‘a’ is a rigid type variable bound by
        the type signature for:
          foldJ :: ([(String, b)] -> b)
                   -> ([b] -> b) -> forall a. JsonValue a => (a -> b) -> Json -> b
        at Json.hs:(333,1)-(335,47)
    • In the first argument of ‘value’, namely ‘x’
      In the expression: value x
      In an equation for ‘foldJ’:
          foldJ object array value (JValue x) = value x
    • Relevant bindings include
        x :: a1 (bound at Json.hs:335:34)
        value :: a -> b (bound at Json.hs:335:20)
    |
335 | foldJ object array value (JValue x)   = value x
    |                                               ^
Failed, one module loaded.

这真的让我很困惑。我用过打字孔,一切看起来都是正确的类型,但是当我把它们放在一起时,没有任何效果

标签: haskell

解决方案


与往常一样,签名被解析为右关联,即它是

foldJ :: ([(String, b)] -> b)
         -> ( ([b] -> b)
              -> ( ∀ a . (JsonValue a)
                      => (a -> b) -> (Json -> b)
                 )
            )

并不是说 ∀ 量化了第二次应用的结果。因此它处于协变位置,这意味着使用该函数的人可以选择什么类型a。事实上你的签名相当于

foldJ :: JsonValue a
  => ([(String, b)] -> b) -> ([b] -> b) -> (a -> b) -> (Json -> b)

但这不是你想要表达的:调用者无法选择类型,因为它隐藏在 json 结构中!

您真正想要的是让 quantor 只运行在a -> b 参数上,即调用者必须提供适用于任何类型 a的参数。

foldJ :: ([(String, b)] -> b)
         -> ( ([b] -> b)
              -> ( (∀ a . (JsonValue a) => (a -> b))
                    -> (Json -> b)
                 )
            )

推荐阅读