我在我的程序中编译了以下代码:
{-# 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
,就像我上面没有记录语法一样。我怎么能这样做?
答案 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实例。)