有没有一种方便的方法来使用 mono-traversable 构造更大类型级别的 Peano 数?
Is there a convenient way to construct larger type level Peano numbers using mono-traversable?
mono-traversable 包使用 MinLen
类型级别的 Peano 数。我可以使用链式 Succ
s:
构造它们
toMinLen [1,2,3] :: Maybe (MinLen (Succ (Succ Zero)) [Int])
但这很快就会失控:
toMinLen [1,2,3] :: Maybe (MinLen (Succ (Succ (Succ (Succ (Succ Zero))))) [Int])
有没有方便的方法来构造更大的皮亚诺数?我看到 GHC 有一个 TypeLiterals 扩展,但我不确定我是否可以在这里使用它。或者,我可以制作类似的同义词:
type One = Succ Zero
type Two = Succ One
等等;类似的东西已经存在于某处了吗?
您可以移植 type-natural
's QuasiQuoter
s.
这种东西有一个包:type-level。这有点吓人,我还没有真正探索过它。但是你不需要那么多的力量,所以你可以自己动手做一些简单的事情。
如果您愿意使用 UndecidableInstances
,事情就很简单了。大致是这样的(我不知道那个库中自然是如何定义的;如果它不使用 DataKinds
,你可能必须使用 *
类型而不是 Nat
kind,你可能不得不写 'Succ
和 'Zero
而不是 Succ
和 Zero
-- 这方面我不太清楚):
{-# LANGUAGE UndecidableInstances, TypeFamilies, DataKinds, TypeOperators #-}
module TAR where
-- You wouldn't actually use this line, because the library
-- provides the naturals
data Nat = Zero | Succ Nat
-- Digits and Ten to get things started
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
type Four = Succ Three
type Five = Succ Four
type Six = Succ Five
type Seven = Succ Six
type Eight = Succ Seven
type Nine = Succ Eight
type Ten = Succ Nine
type family Plus (a::Nat) (b::Nat) where
Plus Zero n = n
Plus (Succ m) n = Plus m (Succ n)
type family Times (a::Nat) (b::Nat) where
Times Zero n = Zero
Times (Succ m) n = Plus n (Times m n)
type Decimal (a::[Nat]) = Decimal' a Zero
type family Decimal' (a::[Nat]) (n::Nat) where
Decimal' '[] n = n
Decimal' (a ': bs) n = Decimal' bs (Plus a (Times Ten n))
然后你可以这样写
Decimal '[One, Two, Five]
表示 125。
TypeLits
非常适合类型级数字。此外,仅将其用于语法糖并保持底层特定于库的实现不变也很容易。
{-# LANGUAGE
UndecidableInstances, TypeFamilies,
DataKinds, TypeOperators #-}
import qualified GHC.TypeLits as TL
data Nat = Zero | Succ Nat
newtype MinLen (n :: Nat) a = MinLen a
我们必须定义一个将文字转换为数字类型的类型族:
type family Lit (n :: TL.Nat) :: Nat where
Lit 0 = Zero
Lit n = Succ (Lit (n TL.- 1))
现在您可以在需要 Nat
文字时使用 Lit n
。在 GHCi 中:
>:kind! MinLen (Lit 3)
MinLen (Lit 3) :: * -> *
= MinLen ('Succ ('Succ ('Succ 'Zero)))
mono-traversable 包使用 MinLen
类型级别的 Peano 数。我可以使用链式 Succ
s:
toMinLen [1,2,3] :: Maybe (MinLen (Succ (Succ Zero)) [Int])
但这很快就会失控:
toMinLen [1,2,3] :: Maybe (MinLen (Succ (Succ (Succ (Succ (Succ Zero))))) [Int])
有没有方便的方法来构造更大的皮亚诺数?我看到 GHC 有一个 TypeLiterals 扩展,但我不确定我是否可以在这里使用它。或者,我可以制作类似的同义词:
type One = Succ Zero
type Two = Succ One
等等;类似的东西已经存在于某处了吗?
您可以移植 type-natural
's QuasiQuoter
s.
这种东西有一个包:type-level。这有点吓人,我还没有真正探索过它。但是你不需要那么多的力量,所以你可以自己动手做一些简单的事情。
如果您愿意使用 UndecidableInstances
,事情就很简单了。大致是这样的(我不知道那个库中自然是如何定义的;如果它不使用 DataKinds
,你可能必须使用 *
类型而不是 Nat
kind,你可能不得不写 'Succ
和 'Zero
而不是 Succ
和 Zero
-- 这方面我不太清楚):
{-# LANGUAGE UndecidableInstances, TypeFamilies, DataKinds, TypeOperators #-}
module TAR where
-- You wouldn't actually use this line, because the library
-- provides the naturals
data Nat = Zero | Succ Nat
-- Digits and Ten to get things started
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
type Four = Succ Three
type Five = Succ Four
type Six = Succ Five
type Seven = Succ Six
type Eight = Succ Seven
type Nine = Succ Eight
type Ten = Succ Nine
type family Plus (a::Nat) (b::Nat) where
Plus Zero n = n
Plus (Succ m) n = Plus m (Succ n)
type family Times (a::Nat) (b::Nat) where
Times Zero n = Zero
Times (Succ m) n = Plus n (Times m n)
type Decimal (a::[Nat]) = Decimal' a Zero
type family Decimal' (a::[Nat]) (n::Nat) where
Decimal' '[] n = n
Decimal' (a ': bs) n = Decimal' bs (Plus a (Times Ten n))
然后你可以这样写
Decimal '[One, Two, Five]
表示 125。
TypeLits
非常适合类型级数字。此外,仅将其用于语法糖并保持底层特定于库的实现不变也很容易。
{-# LANGUAGE
UndecidableInstances, TypeFamilies,
DataKinds, TypeOperators #-}
import qualified GHC.TypeLits as TL
data Nat = Zero | Succ Nat
newtype MinLen (n :: Nat) a = MinLen a
我们必须定义一个将文字转换为数字类型的类型族:
type family Lit (n :: TL.Nat) :: Nat where
Lit 0 = Zero
Lit n = Succ (Lit (n TL.- 1))
现在您可以在需要 Nat
文字时使用 Lit n
。在 GHCi 中:
>:kind! MinLen (Lit 3)
MinLen (Lit 3) :: * -> *
= MinLen ('Succ ('Succ ('Succ 'Zero)))