【问题标题】:HList with DataKinds, kind not promotable带有 DataKinds 的 HList,种类不可推广
【发布时间】:2015-01-19 03:37:52
【问题描述】:

我有这段代码 sn-p,它使用了大量的 GHC 扩展:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

import GHC.Exts (Constraint)

data HList :: [*] -> * where
    Nil  :: HList '[]
    Cons :: a -> HList l -> HList (a ': l)

type family All (p :: * -> Constraint) (xs :: HList [*]) :: Constraint where
    All p Nil = ()
    All p (Cons x xs) = (p x, All p xs)

GHC 抱怨说:

 ‘HList’ of kind ‘[*] -> *’ is not promotable
    In the kind ‘HList [*]’

为什么我不能将HList 提升为一种类型?我使用 GHC 7.8.27.11 时遇到同样的错误。

当然,使用内置的'[] 就可以了:

type family All (p :: * -> Constraint) (xs :: [*]) :: Constraint where
    All p '[]       = ()
    All p (x ': xs) = (p x, All p xs)

我想使用我自己的HList 而不是'[],因为实际的HList 支持追加并且看起来像这样:

type family (:++:) (xs :: [*]) (ys :: [*]) where
    '[] :++:  ys = ys
     xs :++: '[] = xs
    (x ': xs) :++: ys = x ': (xs :++: ys)

data HList :: [*] -> * where
    Nil  :: HList '[]
    Cons :: a -> HList l -> HList (a ': l)
    App  :: Hlist a -> HList b -> HList (a :++: b)

编辑:主要目标是让 GHC 推断

(All p xs, All p ys) ==> All p (xs :++: ys)

这样我可以写

data Dict :: Constraint -> * where
    Dict :: c => Dict c

witness :: Dict (All p xs) -> Dict (All p ys) -> Dict (All p (xs :++: ys))
witness Dict Dict = Dict

我曾希望为附加类型级别列表添加显式表示可以帮助我实现这一目标。是否有另一种方法可以让 GHC 相信上述内容?

【问题讨论】:

  • 为什么需要All 才能在HList 上?任何时候你需要它,你可以在[*]上使用All,这是你HList的类型参数,即你可以有frobulate :: All (Frobable xs) => HList xs -> Frob
  • 您不能推广 GADT,因为 GHC 类型系统(或者,更像是一种系统)根本不支持它。您的第二个 HList 并不是特别有意义,因为每个列表都有多种表示形式,并且无论如何您都无法在 :++: 上进行模式匹配。我猜你最终想写All p (xs :++: ys) = All p xs :++: All p ys 之类的东西,但这行不通。您是否正在尝试使用第二个 HList 解决实际问题?
  • 我不明白您为什么需要将 HList 提升为 kind。为什么不能使用 '[] ?您可以轻松附加“[]”。
  • @user2407038:感谢您提供有用的信息。你是完全正确的,我希望能够说服 GHC (All p xs, All p ys) => All p (xs :++: ys) => ...,我希望添加一个显式的附加表示会有所帮助。我对您的评论思考得越多,我开始看到您提到的一些问题(缺乏独特的表现形式)。我还有其他方法可以证明(All p xs, All p ys) => All p (xs :++: ys)吗?

标签: haskell gadt type-families data-kinds constraint-kinds


【解决方案1】:

我现在看到问题是如何编写(All p xs, All p ys) => All p (xs :++: ys) 的证明。答案当然是归纳法!

我们真正要写的函数的类型是

allAppend :: (p :: Constraint) -> (xs :: [*]) -> (ys :: [*]) 
          -> (All p xs, All p ys) -> All p (xs :++: ys)

但是 Haskell 没有依赖类型。 “伪造”依赖类型通常意味着有一个见证人携带一个类型存在的证据。这让事情变得有些乏味,但目前没有其他办法。我们已经有一个列表的见证人xs - 它正是HList xs。对于约束,我们将使用

data Dict p where Dict :: p => Dict p

那么我们可以把蕴含写成一个简单的函数:

type (==>) a b = Dict a -> Dict b 

所以我们的类型变成了:

allAppend :: Proxy p -> HList xs -> HList ys 
          -> (All p xs, All p ys) ==> (All p (xs :++: ys))

函数的主体非常简单——注意allAppend 中的每个模式如何匹配:++: 定义中的每个模式:

allAppend _ Nil _  Dict = Dict  
allAppend _ _  Nil Dict = Dict 
allAppend p (Cons _ xs) ys@(Cons _ _) Dict = 
  case allAppend p xs ys Dict of Dict -> Dict 

相反的蕴涵All p (xs :++: ys) => (All p xs, All p ys) 也成立。实际上,函数定义是相同的。

【讨论】:

  • 这看起来很有希望。我曾尝试过一种“假依赖类型”的方法,但被卡住了。当我有机会尝试您的建议时,我会更新此线程。谢谢!
  • 您已经回答了我最初的问题,allAppend 的工作方式与宣传的一样。我认为它让我走上了正确的道路。不幸的是,我遇到了一些与我原始问题中未包含的其他相关代码相关的障碍。我可能会打开另一个与此相关的 SO 问题
猜你喜欢
  • 1970-01-01
  • 2020-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-31
相关资源
最近更新 更多