正如问题的标题所示,我想知道什么是Levity多态性,其动机是什么?我知道此页面中包含一些详细信息,但其中的大多数解释都超出了我的头脑。:)
尽管此页面友好一些,但我仍然无法理解其背后的动机。
Answers:
注意:此答案基于有关Levity讨论的最新观察结果。有关Levity多态性的所有内容目前仅在GHC 8.0版本候选版本中实现,因此可能会发生变化(例如,参见#11471)。
TL; DR:这是一种使函数在提升和未提升类型上多态的方法,而常规函数则无法实现。例如,下面的代码由于Int#具有kind #,所以没有使用常规多态进行类型检查,但是其中的类型变量id具有kind *:
{-# LANGUAGE MagicHash #-}
import GHC.Prim
example :: Int# -> Int#
example = id -- does not work, since id :: a -> a
Couldn't match kind ‘*’ with ‘#’
When matching types
a0 :: *
Int# :: #
Expected type: Int# -> Int#
Actual type: a0 -> a0
In the expression: id
注意,(->)仍然使用一些魔术。
在我开始回答这个问题之前,让我们退后一步,转到最常用的功能之一($)。
什么是($)类型?嗯,根据Hackage和报告,
($) :: (a -> b) -> a -> b
但是,这还不是100%完成。这是一个方便的小谎言。问题在于多态类型(如a和b)具有种类*。但是,(库)开发人员($)不仅希望将其用于具有kind的类型*,还希望将其用于kind的类型#,例如
unwrapInt :: Int -> Int#
虽然Int有善良*(可以是底部),但Int#有善良#(根本不能是底部)。仍然,以下代码类型检查:
unwrapInt $ 42
那不行 还记得返回类型($)吗?它是多态的,而多态类型却是善良的*,不是#!那为什么行得通呢?首先,它是一个bug,然后是一个黑客(ghc-dev邮件列表中Ryan Scott的邮件摘录):
那么为什么会这样呢?
长答案是,在GHC 8.0之前的类型签名中
($) :: (a -> b) -> a -> b,b实际上不是实物*,而是实物OpenKind。OpenKind是一种可怕的骇客,它允许提升类型(kind*)和未提升类型(kind#)都居住,这就是类型检查的原因(unwrapInt $ 42)。
那么($),GHC 8.0中的新类型是什么?它的
($) :: forall (w :: Levity) a (b :: TYPE w). (a -> b) -> a -> b
-- will likely change according to Richard E.
要了解它,我们必须查看Levity:
data Levity = Lifted | Unlifted
现在,我们可以认为($)具有以下两种类型之一,因为只有两种选择w:
-- pseudo types
($) :: forall a (b :: TYPE Lifted). (a -> b) -> a -> b
($) :: forall a (b :: TYPE Unlifted). (a -> b) -> a -> b
TYPE是一个神奇的常数,它重新定义了种*和#作为
type * = TYPE Lifted
type # = TYPE Unlifted
对种类的量化也相当新,并且是Haskell中依赖类型集成的一部分。
Levity多态性的名称来自于以下事实:您现在可以在提升类型和未提升类型上编写多态函数,而以前的多态性限制是不允许/可能的。同时也摆脱了OpenKind黑客攻击。关于这两种处理,这确实是“正当的”。
顺便说一句,您并不孤单。甚至西蒙·佩顿·琼斯(Simon Peyton Jones)都说有必要创建一个Levity Wiki页面,而Richard E.(当前的实现者)表示,该Wiki页面需要对当前过程进行更新。
GHC.Types,是ghc-primGHC随附的库的一部分。($) :: forall (w1 w2 :: Levity) (a :: TYPE w1) (b :: TYPE w2). (a -> b) -> a -> b,即为什么($)不应该将其与带有未修饰参数的函数一起使用?