用约束类型写GADT记录

时间:2014-02-02 01:32:27

标签: haskell record type-constraints gadt

我在我的程序中编译了以下代码:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}

class (Show (Data a)) => HasData (a :: *) where
    type Data a :: *

data Foo :: * -> * where
    Foo :: (HasData a) => String -> Data a -> Int -> Foo a -- bunch of args

deriving instance Show (Foo a)

由于Foo构造函数的参数数量可能很多,我想使用记录语法编写代码,但我无法弄清楚如何使用GADT语法(GHC弃用数据类型上下文),所以我试图避免它们):

data Foo :: * -> * where
    Foo {
        getStr :: String,
        getData :: Data a, -- want (HasData a)
        getInt :: Int
    } :: Foo a -- want (HasData a)

我需要在a的构造函数中约束Foo,就像我上面没有记录语法一样。我怎么能这样做?

1 个答案:

答案 0 :(得分:16)

您可以通过在构造函数类型中声明记录析构函数来实现此目的:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

data Foo :: * -> * where
    Foo :: HasData a => { -- look - the context declared upfront
        getStr :: String, -- wow - function declaration in type sig!
        getData :: Data a, -- only allowed with a HasData instance
        getInt :: Int
    } -> Foo a 

但我怀疑有一种更简单的方法来实现你打算做的事情,除非你做的事情比a更狡猾。这是一种坚持可显示数据的简单方法:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

data Bar a where
    Bar :: Show a => {
        getStr' :: String,
        getData' :: a, -- can Show 
        getInt' :: Int
    } -> Bar a -- have (Show a)
deriving instance Show (Bar a)

这种方式的好处是可以处理任意Showable数据,而无需创建HasData类的实例,也不使用所有这些编译器编译指示,但如果你的HasData类只是帮助,那对你只有帮助那里的东西可以表现出来。你可能有一些我没有看到的更深层的目的。

(您可以考虑完全删除Show上下文,除了您实际使用它的地方,更简化并允许您创建Functor实例。这会更简单,根本不需要任何编译器编译指示多年来,我变得更加注重保持一般性并制作Functor实例。)