【发布时间】:2018-04-28 09:58:19
【问题描述】:
我有一个代表相同想法的 GADT 构造函数,但我需要其中的两个,因为类型根据上下文是灵活的。看这个人为的例子:
data A
data B
data C
data Thing a where
AFoo :: String -> Thing A
Bar :: Float -> Thing A
BFoo :: String -> Thing B
Baz :: Int -> Thing B
Bah :: Char -> Thing C
AFoo 和BFoo 代表相同的底层数据概念:一些带有字符串的数据。但在这种情况下,它不仅如此。理想情况下,AFoo 和BFoo 将合并到Foo 中,因为它们代表完全相同的东西,但我需要一个像Thing A 这样的类型和一个像Thing B 这样的类型。如前所述,可以使用Foo 的某些上下文需要Thing A,而有些则需要Thing B,因此为了满足类型系统,每个上下文都需要存在一个构造函数。
如果需要,我当然可以通过切换构造函数来编写一个“转换”为所需类型的函数:
fooAsThingA :: Thing a -> Maybe (Thing A)
fooAsThingA t@(AFoo _) = Just t
fooAsThingA (BFoo s) = Just $ AFoo s
fooAsThingA _ = Nothing
(fooAsThingB 也是如此)
但这很丑陋,因为它需要我必须传播的Maybe,因为并非所有Thing Bs 都可以变成Thing As(实际上,只有BFoo 可以)。
理想情况下,我想写:
data A
data B
data C
data Thing a where
Foo :: String -> Thing ??
Bar :: Float -> Thing A
Baz :: Int -> Thing B
Bah :: Char -> Thing C
但我不清楚我会用什么代替??。大概它是表示A 和B 联合的某种方式(也许这需要更改此 GADT 的其他构造函数的类型,但这很好)。
为了清楚起见,如果上述内容有效,我希望给出以下功能:
processThingA :: Thing A -> String
processThingB :: Thing B -> Int
processThingC :: Thing C -> Float
然后,我就可以做以下所有事情了:
processThingA $ Foo "Hello"
processThingB $ Foo "World"
processThingA $ Bar 3.14
processThingB $ Baz 42
processThingC $ Bah '\n'
tl;dr 在第一个 sn-p 中,是否可以将 AFoo 和 BFoo 合并到一个类型为 Thing A 和 Thing B 的 Foo 中?
编辑:注意:除了A 和B 之外,还有其他EmptyDataDecls 用于Thing a。我添加了C 作为示例。
【问题讨论】:
-
data Thing a where Foo :: String -> Thing a ...怎么样?或者您可以使用DataKinds对其进行一些限制:data ThingType = A | B; data Thing (a :: ThingType) where Foo :: String -> Thing a ...。这些适合您的用例吗? -
@DavidYoung 哦,这是一个有趣的解决方案。我没怎么玩过
DataKinds。我确实有其他类型(更新问题以添加Thing C),我不想加入这个“联盟”,所以我认为第一个不会起作用。但是如果我做了data AOrB = A | B; data C是否有办法使用DataKinds来限制Foo构造函数的类型? -
您可以使用
Foo :: String -> AorB t -> Thing t,其中AorB t是合适的GADT,仅由JustA :: AorB A和Just B :: AorB B填充。这不是很方便,并且有一些内存开销,但它可以很好地工作。原则上,您甚至可以使用类型类来保持该见证“隐式”。开销仍然存在,但不必显式传递。 -
@chi 谢谢!那么例如,我必须做
Bar :: Float -> Thing JustA?对于Bah,我会保留data C并让它成为Bah :: Char -> Thing C?我并不太担心内存开销,但我有点恼火,因为这需要对一个额外的无用值进行模式匹配。您能否详细说明如何使用类型类来完成此操作?这对我来说不是很明显。 -
@chi 另外,在尝试过这个之后,看起来这只是将问题推向了
AorB值。如何实例化Foo?Foo "abc" JustA只能在Thing (AorB A)上下文中使用。似乎我仍然需要一个类似“运行时转换”的东西来将Foo "abc" JustA更改为Foo "abc" JustB以便能够将其传递给期望Thing (AorB B)的函数(这有前面提到的问题,即过于宽泛类型为Thing a -> Thing (AorB A),需要Maybe)。有没有办法避免这种情况?