Haskell 无法从 GADT 构造函数中找出类型

标签 haskell gadt type-families

提前为这个长示例道歉,我无法找到一个更短的示例。

让我们定义一个类型类 Box,它只包含另一种类型,即 Content

{-# LANGUAGE GADTs                     #-}
{-# LANGUAGE TypeFamilies              #-}

class Box t where
    type Content t

data IntBox = IntBox
data StringBox = StringBox

让我们写几个实例:

instance Box IntBox where
    type Content IntBox = Int

instance Box StringBox where
    type Content StringBox = String

data JointBox a b = JointBox a b

instance (Box a, Box b) => Box (JointBox a b) where
    type Content (JointBox a b) = Either (Content a) (Content b)

到目前为止,所有内容均已编译并运行。输入 GADT。我想要一个从盒子及其内容中构造出来的代数数据类型。构造函数完全决定盒子类型。

data ABox t where
    AnIntBox :: IntBox -> ABox IntBox
    AStringBox :: StringBox -> ABox StringBox
    AnIntOrStringBox :: JointBox IntBox StringBox -> ABox (JointBox IntBox StringBox)

现在,在我看来,这意味着通过对 ABox 的构造函数进行模式匹配,应该确定框的类型及其内容。但事实似乎并非如此:

frobABox :: (Content t) ->  ABox t              -> IO ()
frobABox     int           (AnIntBox _)         =  print $ int + 3
frobABox     string        (AStringBox _)       =  putStrLn $ reverse string
frobABox    (Left int)     (AnIntOrStringBox _) =  print $ int + 6
frobABox    (Right string) (AnIntOrStringBox _) =  putStrLn $ reverse string

这会引发很多错误,其中这两个对我来说似乎是最重要的:

GADTAndTypeClassBug.hs:29:14:
    Couldn't match expected type ‘Content t’
                with actual type ‘Either t0 t1’
    The type variables ‘t0’, ‘t1’ are ambiguous
    Relevant bindings include
      frobABox :: Content t -> ABox t -> IO ()
        (bound at GADTAndTypeClassBug.hs:27:1)
    In the pattern: Left int
    In an equation for ‘frobABox’:
        frobABox (Left int) (AnIntOrStringBox _) = print $ int + 6

GADTAndTypeClassBug.hs:30:14:
    Couldn't match expected type ‘Content t’
                with actual type ‘Either t2 t3’
    The type variables ‘t2’, ‘t3’ are ambiguous
    Relevant bindings include
      frobABox :: Content t -> ABox t -> IO ()
        (bound at GADTAndTypeClassBug.hs:27:1)
    In the pattern: Right string
    In an equation for ‘frobABox’:
        frobABox (Right string) (AnIntOrStringBox _)
          = putStrLn $ reverse string

GADTAndTypeClassBug.hs:30:71:
    Couldn't match expected type ‘[Char]’ with actual type ‘t3’
      ‘t3’ is untouchable
        inside the constraints (t ~ JointBox IntBox StringBox)
        bound by a pattern with constructor
                   AnIntOrStringBox :: JointBox IntBox StringBox
                                       -> ABox (JointBox IntBox StringBox),
                 in an equation for ‘frobABox’
        at GADTAndTypeClassBug.hs:30:29-46
    Relevant bindings include
      string :: t3 (bound at GADTAndTypeClassBug.hs:30:20)
    In the first argument of ‘reverse’, namely ‘string’
    In the second argument of ‘($)’, namely ‘reverse string’

一个没有类型族的更简单的例子可以工作:

data UnitOrEither t where
    AUnit :: () -> UnitOrEither ()
    AnEither :: Either String Int -> UnitOrEither (Either String Int)

frob :: UnitOrEither t -> IO ()
frob   (AUnit _)       =  print ()
frob   (AnEither _)    =  print "Either"

那么问题出在哪里呢?

最佳答案

GADT 模式匹配的细化是从左到右进行的。因此,因匹配 frobABox 的第二个参数而产生的类型细化将不适用于第一个参数。

您可以通过翻转frobABox的参数来编译代码:

frobABox' :: ABox t              -> Content t -> IO ()
frobABox' (AnIntBox _)          int           =  print $ int + 3
frobABox' (AStringBox _)        string        =  putStrLn $ reverse string
frobABox' (AnIntOrStringBox _) (Left int)     =  print $ int + 6
frobABox' (AnIntOrStringBox _) (Right string) =  putStrLn $ reverse string

frobABox :: (Content t) ->  ABox t              -> IO ()
frobABox = flip frobABox'

关于Haskell 无法从 GADT 构造函数中找出类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33916039/

相关文章:

haskell - 从包装器中动态匹配嵌套的 GADT

haskell - 文件开头无用的种类相等错误

haskell - 为什么 GHC 在使用 Coercible 约束时会自相矛盾?

haskell - 修复 OCaml 中的数据类型

haskell - 奇怪的 Haskell/GHC 输出

haskell - 具有动态请求/响应类型的管道?

haskell - 如何创建一个类型类来表示包含可提取 `Num` 实例类型的容器?

haskell - 如何理解 Control.Arrow 的 '&&&' 运算符的 Haskell 类型签名

parsing - 解析时,我需要添加什么才能将 monadUserState 与 alex 一起使用?

haskell - 自动派生 GADT 的展示实例