2

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

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

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

4

2 回答 2

10

这种类型怎么样?

newtype Pred a = Pred (a -> Bool)

还是这个?

data Proxy a = Proxy

没有办法a摆脱Pred a. 你只能把as 放进去。同样,没有办法从a 中a取出 a Proxy a,因为里面没有任何as 。

所以get :: forall f a. f a -> a一般来说函数是不存在的。您需要使用类型类来区分f可以从中提取的类型a和不能从中提取的类型。

于 2016-10-27T21:35:36.133 回答
3

好吧,那种不受约束的泛型get肯定行不通。这也将允许您VoidConst () :: Const () 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

X现在, Yand的实际实现Z可以只使用默认签名并减少对底层类型表示的提取。为此,这样定义类:

{-# LANGUAGE DefaultSignatures #-}

class Get τ where
  get :: τ a -> a
  default get :: (Generic1 τ, Get (Rep1 τ)) => τ a -> a
  get = get . from1
于 2016-10-27T22:46:22.223 回答