自动派生GADT的show实例

时间:2017-05-22 13:14:58

标签: haskell gadt deriving

假设我有一个复杂的GADT,它有许多隐藏的类型参数作为构造函数:

data T where
  A :: Num n => n -> T
  B :: (Num n, Integral m) => n -> m -> T
  C :: Floating a => [a] -> T
  -- and so on
  Z :: Num n => n -> n -> T

我想让这个数据类型显示而不必手动编写实例。问题是,由于Show不再是Num的超类,因此添加一个简单的deriving instance Show T并不足以让编译器推断它必须添加{{} 1}}约束所有内部隐藏类型参数。

对于每个隐藏类型参数,它输出类似

的内容
Show

向数据类型添加Could not deduce (Show n) arising from a use of 'showsPrec' from the context Num n bound by a pattern with constructor A :: forall n. Num n => n -> T ... Possible fix: add (Show n) to the context of the data constructor 'A' 约束也不是一个选项,因为它限制了Show的可能居民。似乎T应该在隐藏数据类型上引入约束deriving instanec Show T,尽管我不确定。

我该如何解决这个问题?

1 个答案:

答案 0 :(得分:6)

我有一个有趣的想法,不确定它有多实用。但是,如果您希望T在参数可显示时显示,但也可以与不可显示的参数一起使用,则可以使用T在约束上参数化ConstraintKinds

{-# LANGUAGE GADTs, ConstraintKinds #-}

import Data.Kind

data T :: (* -> Constraint) -> * where
    A :: (Num n, c n) => n -> T c
    B :: (Num n, c n, Integral m, c m) => n -> m -> T c
    ...

然后T Show将显示... 也许

deriving instance Show (T Show)

(扩展名为StandaloneDeriving)可以使用,但至少原则上可以显示T,您可以手动编写实例。

虽然我的实际建议是要重新确定存在感。存在类型等同于其观察的集合。例如,如果您有类似

的类
class Foo a where
   getBool :: a -> Bool
   getInt  :: a -> Int

然后是存在主义

data AFoo where
   AFoo :: Foo a => a

完全等同于(Bool,Int),因为对于您不知道其类型的Foo,您唯一能做的就是在其上调用getBoolgetInt它。您在数据类型中使用Num,并且Num 没有观察,因为如果您有a Num a,那么您就是唯一的通过调用Num的方法可以做得更多a,而且没有任何具体的东西。那么你的A构造函数

A :: (Num n) => n -> T

给你没有,你也可以说

A :: T
另一方面,

IntegraltoInteger作为观察。所以你可以替换

B :: (Num n, Integral m) => n -> m -> T

B :: Integer -> T

(我们丢失了n参数,并将m替换为Integer)。我不认为这在技术上是等同的,因为我们可以以不同于Integral的方式实现其操作,但是我们在这一点上变得非常技术化并且我怀疑你是否需要它(我对它感兴趣)如果你这样做。)