Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 存储和检索某个类型的对象-可能吗?如果是,如何进行?_Haskell_Typeclass - Fatal编程技术网

Haskell 存储和检索某个类型的对象-可能吗?如果是,如何进行?

Haskell 存储和检索某个类型的对象-可能吗?如果是,如何进行?,haskell,typeclass,Haskell,Typeclass,假设我有一些类型类Foo和一些数据类型FooInst,它们是Foo的实例: class Foo a where foo :: a -> String data FooInst = FooInst String instance Foo FooInst where foo (FooInst s) = s 现在,我想定义一个数据类型,它存储类型在Footypeclass中的对象,并且能够从该数据类型中提取该对象并使用它 我找到的唯一方法是使用GADTs和Rank2Types语

假设我有一些类型类
Foo
和一些数据类型
FooInst
,它们是
Foo
的实例:

class Foo a where
  foo :: a -> String

data FooInst = FooInst String

instance Foo FooInst where
    foo (FooInst s) = s
现在,我想定义一个数据类型,它存储类型在
Foo
typeclass中的对象,并且能够从该数据类型中提取该对象并使用它

我找到的唯一方法是使用GADTs和Rank2Types语言扩展并定义如下数据类型:

data Container where
  Container :: { content :: Foo a => a } -> Container
但是,问题是,我无法使用
content
选择器从容器中取出内容:

cont :: Container
cont = Container{content = FooInst "foo"}

main :: IO ()
main = do
  let fi = content cont
  putStrLn $ foo fi
结果导致编译错误

Cannot use record selector ‘content’ as a function due to escaped type variables
Probable fix: use pattern-matching syntax instead
但是当我修改
时,让…

let Conainer fi = cont
我犯了一个相当有趣的错误

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.
如果我再次尝试修改
let…
行以使用用例表达式

let fi = case cont of
           Container x -> x
我得到一个不同的错误

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
那么,如何存储类型化的对象并将其检索回来呢?例如

main :: IO ()
main = do
  case cont of
    Container fi -> putStrLn $ foo fi
您需要将存在类型字段
fi
的使用封装在一个表达式中,该表达式的类型不依赖于
fi
的类型;这里的
putStrLn$foo-fi
类型为
IO()

这个示例非常无用,因为对
容器
内容
字段所能做的唯一一件事就是调用
foo
,所以在构建容器之前,您最好只调用
foo
,并给出字段类型
字符串。但是更有趣的是,如果
Foo
具有类似
a->a->a
的类型的操作,或者
Container
具有包含相同存在量化变量的类型的多个字段,等等

data Container where
    Container :: {content :: Foo a => a} -> Container
类型类约束甚至没有强制执行。就是

void :: Container
void = Container {content = 42 :: Int}
类型检查,即使
42::Int
不是
Foo
的实例

但如果您改为:

data Container where
    Container :: Foo a => {content :: a} -> Container
  • 您不再需要
    Rank2Types
    语言扩展
  • 强制执行类型类约束;因此,上面的
    void
    示例将不再进行类型检查
  • 此外,您可以通过模式匹配对内容调用
    foo
    (或任何其他具有签名的函数
    fooa=>a->…
    ):

    case cont of Container {content = a} -> foo a
    

  • 第4节有一个很好的例子,存储了多个具有可显示实例的项。注意不要落入这种已知的反模式:@pdexter据我所知,我对
    容器
    的定义实际上等同于
    可显示
    ,除了记录语法。这是记录语法吗?那么是什么让我觉得不同呢?我觉得奇怪的是,只是一个不同的语法也改变了语义……您使用的是GADTs,而示例没有。这只是我第一眼看到的一个区别。因此,这不仅仅是不同的语法,而是不同的理论。@pdexter该示例在最后显示,它可以使用GADT等价地表示。但贝扎德·努里的回答或多或少地解释了这种差异。无论如何,谢谢你。你的代码对我来说不起作用,因为使用“Foo”可能的修复:将(Foo a)添加到数据构造函数“Container”的上下文中,我没有注意到你也定义了错误的
    Container
    。贝扎德,努里的回答和你的意思一样。