【问题标题】:Function returning result of any constructor of a GADT函数返回 GADT 的任何构造函数的结果
【发布时间】:2018-03-03 06:11:36
【问题描述】:

当我尝试创建一个返回 Thing a 的函数(其中 Thing 是 GADT)时,我目前正在与类型检查器发生争执。一个最小的人为示例:

{-#LANGUAGE GADTs, EmptyDataDecls #-}
module Main where

-- Define a contrived GADT
data TFoo
data TBar
data Thing a where
  Foo :: Int -> Thing TFoo
  Bar :: String -> Thing TBar

combine :: [Thing a]
combine = [Foo 1, Bar "abc"]

main :: IO ()
main = undefined

类型检查器对a 不匹配TBar 感到不满。大概这是因为它已经推断出aTFoo。但是,这令人惊讶,因为您可以使用常规 sum 类型:

data Thing = Foo Int | Bar String

combine :: [Thing]
combine = [Foo 1, Bar "abc"]

是否有返回 GADT 参数的类型参数?

在人为示例的上下文之外,我需要 GADT,因此我可以键入某些函数以仅获取 Foo,但在此之前我还需要能够仅返回 Things 的列表。

【问题讨论】:

  • 我认为您的问题是列表必须是同质的。正如您所指出的,您可以拥有[Thing],但您不能在同一个列表中使用两个不同的Thing a
  • @jkeuhlen 对。这对于像 [1, "a"] 这样明显不同的类型是有意义的,但我对 GADT 和 sum 类型之间的奇偶性有点困惑。 Thing a 不是 FooBar 的“最终返回类型”。是否有任何类型包含Foo 1Bar "abc" 的结果?
  • 不,Thing a 不是构造函数的最终返回类型。他们明确地创建了一个Thing TFOO 和一个Thing TBar

标签: haskell typing gadt


【解决方案1】:

你的量词搞混了。

combine :: [Thing a]

表示(forall 隐含在语法中)

combine :: forall a. [Thing a]

本质上,combine 必须是 [Thing a],无论 a 是什么,因为 a 是由调用 combine 的代码选择的。或者,在另一种意义上,

-- psuedo-Haskell
combine :: (a :: *) -> [Thing a]

combine 是一个函数,它接受一个类型作为参数,并承诺构造一个该类型的Things 列表。具有这种类型签名的combine 的唯一可能定义是combine = [],加上一堆像[undefined] 等愚蠢的定义。 combine = [Foo 1] 也不起作用,因为a 没有被推断出来,因为设置combine 的不是a;是用户。

你想要

-- psuedo-Haskell
combine :: [exists a. Thing a]

这意味着“combine 是一个事物列表,每个事物都是某种未知类型的 Thing”(每个 Thing 都可以是不同的类型)。 exists 量词是forall 的反面。这意味着combine 的定义可以设置它想要的任何类型,用户将不得不处理它。 Haskell 不支持开箱即用的exists,所以需要定义一个中间数据类型:

data SomeThing = forall a. SomeThing (Thing a)

使用通用量词创建存在量词的语法有点倒退,但想法是你得到

SomeThing :: forall a. Thing a -> SomeThing

这基本上抹去了a是什么的知识。

然后你可以拥有

combine :: [SomeThing]
combine = [SomeThing $ Foo 1, SomeThing $ Bar "abc"]

【讨论】:

  • 哇!很棒的答案。太感谢了。这完美无缺。有点不幸的是,为了获得 GADT 的好处(只要你可以限制哪些“孩子?”被传递到其他函数中),你现在不能在没有包装的情况下均匀地使用它们。你知道 exists 是将来可能存在于 Haskell 中的东西,还是只是包装了每个人都使用的公认方法?
  • @BaileyParker 接受的方法只是包装。另一种方法是更高级别的类型延续 (forall ret. (forall a. Thing a -> ret) -> ret) (e.g. this is how you must do it in Typescript),但它比这种包装要痛苦得多(而且由于不可预测性,您仍然需要包装)。我认为没有计划将 exists 添加到 Haskell,因为同时拥有 existsforall 可能会使类型检查器过于复杂。
  • 在多次尝试添加暗示性多态性时,您将exist 称为forall。关于受保护的实例化的最新提案并没有丢失所有内容,但实施仍远未完成,更多信息请参见:mail.haskell.org/pipermail/ghc-devs/2018-February/015407.html
  • @ArtemPelenitsyn 对不起,什么? existsforall 是完全相反的。 [forall a. Show a => a] 表示事物列表,其中每个事物都可以是满足 Show 的任何类型。该类型的唯一值是 [][undefined] 等。[exists a. Show a => a] 表示每个事物都有一些 Showable 类型的事物列表。这有很多价值,例如[5, "abc", 'a', [10]]。对于某些任意类型AShow A[forall a. Show a => a][A] 的子类型,是[exists a. Show a => a] 的子类型。
  • @HTNW 你不知道即使 GHC 中的 ExistentialTypes 扩展也使用 forall 关键字?
猜你喜欢
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 2014-07-27
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多