存储和检索某个类型类的对象 - 这可能吗?如果有,怎么样?

zeg*_*jan 3 haskell typeclass

假设我有一些类型类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的东西并将其取回?

beh*_*uri 6

附:

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)
  1. 你不再需要Rank2Types语言扩展.
  2. 强制执行类型类约束; 因此上面的void例子将不再进行类型检查.
  3. 此外,您可以使用模式匹配对内容调用foo(或任何其他带签名的函数Foo a => a -> ...):

    case cont of Container {content = a} -> foo a
    
    Run Code Online (Sandbox Code Playgroud)