14

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

{-# 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)

我需要像上面那样在没有记录语法的情况下限制ain的构造函数。Foo我怎样才能做到这一点?

4

1 回答 1

19

您可以通过在构造函数的类型中声明记录析构函数来实现这一点,如下所示:

{-# 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 实例。)

于 2014-02-02T02:31:51.607 回答