假设我有一些类型类Foo和一些数据类型FooInst是以下实例Foo:
class Foo a where
foo :: a -> String
data FooInst = FooInst String
instance Foo FooInst where
foo (FooInst s) = s
Run Code Online (Sandbox Code Playgroud)
现在,我想定义一种数据类型,该类型存储类型在Foo类型类中的对象,并且能够从该数据类型中提取该对象并使用它.
我找到的唯一方法是使用GADT和Rank2Types语言扩展并定义数据类型,如下所示:
data Container where
Container :: { content :: Foo a => a } -> Container
Run Code Online (Sandbox Code Playgroud)
但问题是,我无法使用content选择器从Container中获取内容:
cont :: Container
cont = Container{content = FooInst "foo"}
main :: IO ()
main = do
let fi = content cont
putStrLn $ foo fi
Run Code Online (Sandbox Code Playgroud)
导致编译错误
Cannot use record selector ‘content’ as a function due to escaped type variables
Probable fix: use pattern-matching syntax instead
Run Code Online (Sandbox Code Playgroud)
但是当我修改let ...线路时
let Conainer fi = cont
Run Code Online (Sandbox Code Playgroud)
我得到一个相当有趣的错误
My brain just exploded
I can't handle pattern bindings for existential or GADT data constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.
Run Code Online (Sandbox Code Playgroud)
如果我再次尝试修改该let ...行以使用case-expression
let fi = case cont of
Container x -> x
Run Code Online (Sandbox Code Playgroud)
我得到了一个不同的错误
Couldn't match expected type ‘t’ with actual type ‘a’
because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor
Container :: forall a. (Foo a => a) -> Container,
in a case alternative
at test.hs:23:14-24
Run Code Online (Sandbox Code Playgroud)
那么,我如何存储一个typeclassed的东西并将其取回?
附:
data Container where
Container :: {content :: Foo a => a} -> Container
Run Code Online (Sandbox Code Playgroud)
甚至没有强制执行类型类约束.那是
void :: Container
void = Container {content = 42 :: Int}
Run Code Online (Sandbox Code Playgroud)
类型检查,即使42 :: Int是没有的一个实例Foo.
但如果你改为:
data Container where
Container :: Foo a => {content :: a} -> Container
Run Code Online (Sandbox Code Playgroud)
Rank2Types语言扩展.void例子将不再进行类型检查.此外,您可以使用模式匹配对内容调用foo(或任何其他带签名的函数Foo a => a -> ...):
case cont of Container {content = a} -> foo a
Run Code Online (Sandbox Code Playgroud)