将此视为@chi答案的附录,以提供对单例方法的额外解释。。。
我建议阅读
Hasochism paper
如果您还没有这样做。特别是,在那篇文章的第3.1节中,他们正好处理了这个问题,并将其作为一个激励示例,说明当隐式单例参数(
SingI
关于@chi的答案
NATTY
Hasochism论文中的type class)是必要的,而不仅仅是方便的。
由于它适用于您的代码,主要问题是
pure
需要一个应该生成的向量长度的运行时表示,以及类型级别变量
n
不符合要求。解决方案是引入一个新的GADT,即“单例”,它提供与升级类型直接对应的运行时值
Next
和
Zero
:
data Natty (n :: Nat) where
ZeroTy :: Natty Zero
NextTy :: Natty n -> Natty (Next n)
我尝试使用与论文大致相同的命名约定:
Natty
是相同的,并且
ZeroTy
和
NextTy
与报纸的
Zy
和
Sy
。
就其本身而言,这个显式单例是有用的。例如,请参见
vchop
在报纸上。此外,我们可以很容易地编写
纯净的
这需要显式单例来完成其工作:
vcopies :: Natty n -> a -> Vector n a
vcopies ZeroTy _ = Empty
vcopies (NextTy n) x = Construct x (vcopies n x)
我们还不能用这个来定义
纯净的
但是,因为
纯净的
的签名由
Applicative
类型,我们无法压缩显式单例
Natty n
在那里。
解决方案是引入隐式单例,它允许我们在需要时通过
natty
函数在以下类型类的上下文中:
class NATTY n where
natty :: Natty n
instance NATTY Zero where
natty = ZeroTy
instance NATTY n => NATTY (Next n) where
natty = NextTy natty
现在,如果我们在
NATTY n
上下文,我们可以调用
vcopies natty
提供
vcopies
具有明确的
整洁时髦的
参数,它允许我们写入:
instance NATTY n => Applicative (Vector n) where
(<*>) = vapp
pure = vcopies natty
使用的定义
vcopies
和
整洁时髦的
以及
vapp
以下内容:
vapp :: Vector n (a -> b) -> Vector n a -> Vector n b
vapp Empty Empty = Empty
vapp (Construct f c) (Construct x d) = Construct (f x) (vapp c d)
请注意一个奇怪之处。我们需要介绍一下
vapp
助手函数,原因不明。以下实例
没有
纳蒂
匹配您的
case
-基于定义和类型检查精细:
instance Applicative (Vector n) where
Empty <*> Empty = Empty
Construct f c <*> Construct x d = Construct (f x) (c <*> d)
pure = error "Argh! No NATTY!"
如果我们添加
纳蒂
要定义的约束
纯净的
:
instance NATTY n => Applicative (Vector n) where
Empty <*> Empty = Empty
Construct f c <*> Construct x d = Construct (f x) (c <*> d)
pure = vcopies natty
定义
(<*>)
不再进行类型检查。问题是
纳蒂n
第二个左侧的约束
(<*>)
case并不自动意味着
NATTY n1
右侧约束(其中
Next n ~ n1
),所以GHC不想让我们打电话
(<*>)
在右侧。在这种情况下,由于约束在第一次使用后实际上并不需要,因此没有
纳蒂
约束,即
vapp
,解决了问题。
@chi在上使用大小写匹配
整洁时髦的
和helper函数
withSingI
作为替代解决方案。这里的等效代码将使用一个helper函数,该函数将显式单例转换为隐式单例
纳蒂
上下文:
withNATTY :: Natty n -> (NATTY n => a) -> a
withNATTY ZeroTy a = a
withNATTY (NextTy n) a = withNATTY n a
允许我们编写:
instance NATTY n => Applicative (Vector n) where
Empty <*> Empty = Empty
Construct f c <*> Construct x d = case (natty :: Natty n) of
NextTy n -> withNATTY n $ Construct (f x) (c <*> d)
pure x = case (natty :: Natty n) of
ZeroTy -> Empty
NextTy n -> Construct x (withNATTY n $ pure x)
这两者都需要
ScopedTypeVariables
和
RankNTypes
。
无论如何,坚持使用helper函数,整个程序如下所示:
{-# LANGUAGE DataKinds, GADTs, KindSignatures #-}
module Vector where
data Nat = Next Nat | Zero
data Vector :: Nat -> * -> * where
Construct :: t -> Vector n t -> Vector ('Next n) t
Empty :: Vector 'Zero t
data Natty (n :: Nat) where
ZeroTy :: Natty Zero
NextTy :: Natty n -> Natty (Next n)
class NATTY n where
natty :: Natty n
instance NATTY Zero where
natty = ZeroTy
instance NATTY n => NATTY (Next n) where
natty = NextTy natty
instance Functor (Vector n) where
fmap f a =
case a of
Construct x b -> Construct (f x) (fmap f b)
Empty -> Empty
instance NATTY n => Applicative (Vector n) where
(<*>) = vapp
pure = vcopies natty
vapp :: Vector n (a -> b) -> Vector n a -> Vector n b
vapp Empty Empty = Empty
vapp (Construct f c) (Construct x d) = Construct (f x) (vapp c d)
vcopies :: Natty n -> a -> Vector n a
vcopies ZeroTy _ = Empty
vcopies (NextTy n) x = Construct x (vcopies n x)
与
singletons
图书馆是:
$(singletons [d|
data Nat = Next Nat | Zero
|])
自动生成单例(使用构造函数
SZero
和
SNat
而不是
零度
和
NatTy
;和类型
SNat公司
而不是
整洁时髦的
)和隐式singleton类(称为
辛格
而不是
纳蒂
以及使用函数
sing
而不是
整洁时髦的
),给出完整的程序:
{-# LANGUAGE DataKinds, GADTs, KindSignatures, TemplateHaskell, TypeFamilies #-}
module Vector where
import Data.Singletons
import Data.Singletons.TH
$(singletons [d|
data Nat = Next Nat | Zero
|])
data Vector :: Nat -> * -> * where
Construct :: t -> Vector n t -> Vector ('Next n) t
Empty :: Vector 'Zero t
instance Functor (Vector n) where
fmap f a =
case a of
Construct x b -> Construct (f x) (fmap f b)
Empty -> Empty
instance SingI n => Applicative (Vector n) where
(<*>) = vapp
pure = vcopies sing
vapp :: Vector n (a -> b) -> Vector n a -> Vector n b
vapp Empty Empty = Empty
vapp (Construct f c) (Construct x d) = Construct (f x) (vapp c d)
vcopies :: SNat n -> a -> Vector n a
vcopies SZero _ = Empty
vcopies (SNext n) x = Construct x (vcopies n x)
更多关于
单件
图书馆的功能和建造方式,我建议阅读
Introduction to Singletons
。