更高种类的功能?

时间:2016-10-27 20:44:27

标签: haskell types data-kinds

假设定义了以下数据类型:

data X a = X {getX :: a}
data Y a = Y {getY :: a}
data Z a = Z {getZ :: a}

必须有三个单独的函数getXgetYgetZ吗?在我看来,可能有一个定义如下的函数:

get :: forall (τ :: (* -> *)) (a :: *). τ a -> a
get (_ x) = x

显然这不是有效的标准Haskell,但GHC有很多扩展,似乎可能有解决方案(RankNTypesExistentialQuantificationDataKinds等。除了避免少量键入的简单原因之外,还有避免记录解决方案创建的命名空间污染的好处。我想这实际上只是一个比使用这样的类型类更隐式的解决方案:

class Get f where
  get :: f a -> a

然而,似乎定义泛型函数比类型类更有用,因为它是隐式定义的事实意味着它可以在更多的地方使用,就像($)或使用(.)。所以我的问题有三个部分:有没有办法实现这个目标,这是一个好主意,如果没有,那么更好的方法是什么?

2 个答案:

答案 0 :(得分:10)

这种类型怎么样?

newtype Pred a = Pred (a -> Bool)

还是这个?

data Proxy a = Proxy

无法从a中获得Pred a。您只能将a放进去。同样,也无法从a中获取Proxy a,因为其中没有任何a。< / p>

因此,函数get :: forall f a. f a -> a通常不存在。您需要使用类型类来区分可以从中提取f的那些类型a和不能从中提取的类型。

答案 1 :(得分:3)

那么,get这种不受约束的泛型类型当然不行。这也允许您从Const () :: Const () Void中提取Void值。

然而,您可以使用generics非常简单地获得此函数的适当约束版本。您仍然需要一个类型类,但不需要定义传统意义上的实例。它最终看起来像这样:

{-# LANGUAGE TypeFamilies, DeriveGeneric, DeriveAnyClass #-}
import GHC.Generics

class Get τ where
  get :: τ a -> a

data X a = X a deriving (Generic1, Get)
data Y a = Y a deriving (Generic1, Get)
data Z a = Z a deriving (Generic1, Get)

要真正实现这一点,我们只需要两个奇怪的表示类型实例:

instance Get f => Get (M1 i t f) where get = get . unM1
instance Get Par1 where get = unPar1

现在XYZ的实际实现只能使用默认签名并将提取减少到基础类型表示。为此,定义类:

{-# LANGUAGE DefaultSignatures #-}

class Get τ where
  get :: τ a -> a
  default get :: (Generic1 τ, Get (Rep1 τ)) => τ a -> a
  get = get . from1