【问题标题】:Generic Instance for GADT in HaskellHaskell 中 GADT 的通用实例
【发布时间】:2016-02-15 20:15:47
【问题描述】:

我正在尝试在 Haskell 中序列化数据类型,并且想知道如何序列化 GADT。我尝试的方法看起来像(使用 Control.DeepSeq):

data Gadt a where 
  Cons1 :: Int -> Gadt Int
  Cons2 :: Bool -> Gadt Bool

instance NFData Cons1 where rnf = genericRnf
instance NFData Cons2 where rnf = genericRnf

deriveSafeCopy ......

但是,这不会编译:GHC 告诉我 Cons1 和 Cons2 不是有效的构造函数。我可以很好地序列化标准数据类型。序列化 GADT 的最佳方式是什么?

我想序列化 GADT 的原因是因为这种数据类型是我用于项目的 Redis 数据库中的一个键。

【问题讨论】:

  • woops .. 我刚刚看到这根本行不通-您定义的情况会产生此功能:Cons1 :: forall a . a -> Gadt Int 所以我认为您真的在尝试做其他事情吗?
  • 您可能想查看instant-generics,它通常用于处理 GADT。我自己还没有深入研究它,但它似乎很有趣。
  • 是的,我想我确实想要 Cons1 :: Int -> Gadt Int。谢谢!

标签: haskell serialization


【解决方案1】:

免责声明

正如@luqui 指出的那样,这并没有回答关于generics 的问题


好吧,原来你的定义有点奇怪(注意Cons1 :: a -> 中的aforall a 并且与Gadt a 中的a 无关 - 我看不出你做了什么尝试做这里 - 抱歉)

基本上你可以把任何你想要的值放在那里

- Cons1 "Hello" -- :: Gadt Int
- Cons2 'c'     -- :: Gadt Bool
- Cons2 [1,2,3] -- :: Gadt Bool

all 会给您一个 Gadt IntGadt Bool - 但是当您对其进行模式匹配时,没有办法使该值有意义,因为它实际上可以是任何东西(包括不是实例的东西NFData 本身)


但原则上,您声明实例的方式与使用普通 ADT 一样 - 即在此处为不同的情况定义 rnf

假设我将您的 GADT 更改为:

data Gadt a where 
  Cons1 :: Int  -> Gadt Int
  Cons2 :: Bool -> Gadt Bool

那么这个实例声明就可以了:

instance NFData (Gadt a) where
    rnf (Cons1 a) = rnf a
    rnf (Cons2 a) = rnf a 

【讨论】:

  • 有用的观察,只是指出它没有回答关于泛型的问题
  • 我仍然无法在 GADT 上制作泛型。如果我尝试让 Gadt 派生泛型,我会得到:“无法生成‘Generic (Gadt a)’的派生实例:构造函数‘Cons1’在其类型中有存在或约束”
  • @TimothyChu 这仍然是Cons1 :: a -> Gadt Int 对吗?` - 问题出在第一部分,我试图解释为什么这里的a 可以是任何东西 (这是编译器抱怨的 existentials - 有一个 good article about this on wiki.Haskell.org
  • @TimothyChu GHC 泛型机制不支持您问题中的构造函数。通常,所有具有索引的 GADT 构造函数都被排除在具有通用实例之外。如果您想要这种类型的泛型,确实有一个具有索引数据类型的泛型库 - 在您的问题的评论中提到了它。
猜你喜欢
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 2015-03-07
  • 2011-10-13
  • 2013-11-25
  • 1970-01-01
相关资源
最近更新 更多