【问题标题】:Functions with higher kinds?具有更高种类的功能?
【发布时间】:2016-10-27 20:44:26
【问题描述】:

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

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

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

【问题讨论】:

  • 如果你的get函数有一个列表(也就是说,如果τ[]),你的get函数会做什么?
  • 好吧,它做了一个不可行的隐含假设。没有理由假设具有类型 * 的最终类型变量的类型具有单个构造函数,该构造函数接受所讨论类型的单个参数。

标签: haskell types data-kinds


【解决方案1】:

这种类型怎么样?

newtype Pred a = Pred (a -> Bool)

还是这个?

data Proxy a = Proxy

无法从Pred a 中获取a。您只能输入as。同样,也无法从Proxy a 中取出a,因为其中没有任何as。

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

【讨论】:

    【解决方案2】:

    好吧,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
    

    【讨论】:

      猜你喜欢
      • 2021-01-29
      • 1970-01-01
      • 1970-01-01
      • 2018-10-02
      • 2018-12-09
      • 2013-04-11
      • 1970-01-01
      • 2020-08-04
      • 2023-03-21
      相关资源
      最近更新 更多