作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我很难说服编译器我的类型是正确的。定期
具有Nat
和Zero
构造函数的Succ
很简单(目标是为长度索引列表(replicate
)编写Vect
函数):
replicate' :: SNat n -> a -> Vect n a
replicate' SZero _ = Nil
replicate' (SSucc n) a = a :> replicate' n a
Nat
非常慢。
sameNat :: forall a b. (KnownNat a, KnownNat b) => SNat a -> SNat b -> Maybe (a :~: b)
sameNat x y
| natVal (Proxy :: Proxy a) == natVal (Proxy :: Proxy b) = Just (unsafeCoerce Refl)
| otherwise = Nothing
replicate'' :: (KnownNat n) => SNat n -> a -> Vect n a
replicate'' n a =
case sameNat n (sing :: Sing 0) of
Just Refl -> Nil
Nothing -> a ::> replicate'' (sPred n) a
Couldn't match type ‘n’
with ‘(n GHC.TypeNats.- 1) GHC.TypeNats.+ 1’
最佳答案
问题是,在n为零的情况下(当在sameNat n (sing :: Sing 0)
上进行模式匹配时),n ~ 0
为您提供了可用的Just Refl
证明,但是如果n不为零,则仅为您提供Nothing
。这完全没有告诉您有关n
的任何信息,因此就类型检查器而言,您可以在Nothing
分支内完全调用相同的东西集,而无需首先调用sameNat
(特别是,您不能使用sPred
,因为这需要该1 <= n
)。
因此,我们需要对提供n ~ 0
证据或1 <= n
证据的事物进行模式匹配。像这样:
data IsZero (n :: Nat)
where Zero :: (0 ~ n) => IsZero n
NonZero :: (1 <= n) => IsZero n
deriving instance Show (IsZero n)
replicate''
:
isZero :: forall n. SNat n -> IsZero n
isZero n = _
replicate'' :: SNat n -> a -> Vect n a
replicate'' n x = case isZero n
of Zero -> Nil
NonZero -> x ::> replicate'' (sPred n) x
isZero
函数上,该函数实际上并没有给我们买任何东西,但是我会坚持使用它,因为将其作为您想要的任何其他归纳定义的基础很方便使用
Nat
进行。
isZero
。我们当然可以用
sameNat
处理零大小写,但这无助于非零大小写。单例包还提供了
Data.Singletons.Decide
,它为您提供了一种基于单例来证明类型相等或不相等的方法。因此,我们可以这样做:
isZero :: forall n. SNat n -> IsZero n
isZero n = case n %~ (SNat @0)
of Proved Refl -> Zero
Disproved nonsense -> NonZero
Proved
的情况很好(基本上与
sameNat
赋予我们
Just Refl
相同)。但是“不等式的证明”以
nonsense
绑定到类型为
(n :~: 0) -> Void
的函数的形式出现,并且如果我们假设全部(没有假名),则该函数的存在“证明”我们不能构造一个
n :~: 0
值,该值证明
n
绝对不是
0
。但这与
1 <= n
的证明相距太远。我们可以看到,如果
n
不为0,那么从自然数的属性来看,它必须至少为1,但是GHC不知道这一点。
Ord
上使用单例的
SNat @1 :%<= n
支持和模式匹配:
isZero :: forall n. SNat n -> IsZero n
isZero n = case (SNat @1) %:<= n
of STrue -> NonZero
SFalse -> Zero
STrue
和
SFalse
只是类型级别
True
和
False
的单例,与原始比较断开了连接。我们从任一方面都没有得到
0 ~ n
或
1 <= n
的证据(同样,通过与
SNat @0
进行比较,也无法使其起作用)。基本上,这是类型检查器布尔盲。
<
或
<=
约束的方式比较单身人士,要么需要切换
Nat
为零还是非零。
isZero :: forall n. SNat n -> IsZero n
isZero n = case n %~ (SNat @0)
of Proved Refl -> Zero
Disproved _ -> unsafeCoerce (NonZero @1)
NonZero
仅包含
n
为1或更大的证据,而没有包含有关
n
的任何其他信息,因此您可以不安全地强制使用1为1或更大的证据。
{-# LANGUAGE DataKinds
, GADTs
, KindSignatures
, ScopedTypeVariables
, StandaloneDeriving
, TypeApplications
, TypeOperators
#-}
import GHC.TypeLits ( type (<=), type (-) )
import Data.Singletons.TypeLits ( Sing (SNat), SNat, Nat )
import Data.Singletons.Prelude.Enum ( sPred )
import Data.Singletons.Decide ( SDecide ((%~))
, Decision (Proved, Disproved)
, (:~:) (Refl)
)
import Unsafe.Coerce ( unsafeCoerce )
data IsZero (n :: Nat)
where Zero :: (0 ~ n) => IsZero n
NonZero :: (1 <= n) => IsZero n
deriving instance Show (IsZero n)
isZero :: forall n. SNat n -> IsZero n
isZero n = case n %~ (SNat @0)
of Proved Refl -> Zero
Disproved _ -> unsafeCoerce (NonZero @1)
data Vect (n :: Nat) a
where Nil :: Vect 0 a
(::>) :: a -> Vect (n - 1) a -> Vect n a
deriving instance Show a => Show (Vect n a)
replicate'' :: SNat n -> a -> Vect n a
replicate'' n x = case isZero n
of Zero -> Nil
NonZero -> x ::> replicate'' (sPred n) x
head'' :: (1 <= n) => Vect n a -> a
head'' (x ::> _) = x
main :: IO ()
main = putStrLn
. (:[])
. head''
$ replicate''
(SNat @1000000000000000000000000000000000000000000000000000000)
'\x1f60e'
unsafeCoerce
的方法不同,此处的复制代码实际上是使用类型检查器来验证其是否根据提供的
Vect n a
构造了
SNat n
,而他们的建议则要求您信任代码执行此操作(实际工作的实质是通过
iterate
依靠
Int
来完成),并且仅确保调用者一致地使用
SNat n
和
Vect n a
。您只需信任的唯一代码(未经编译器检查)是
Refuted _ :: Decision (n :~: 0)
确实在
1 <= n
内暗示了
isZero
(您可以重复使用它们来编写许多其他功能,这些功能需要打开或关闭)
SNat
是否为零)。
Vect
实现更多功能时,您会发现GHC对
Nat
属性不了解的许多“显而易见的”东西是很痛苦的。
Data.Constraint.Nat
包中的
constraints
有很多有用的证明可供您使用(例如,如果您尝试实现
drop :: (k <= n) => SNat k -> Vect n a -> Vect (n - k) a
,则可能最终需要
leTrans
,这样当您知道
1 <= k
然后也可以
1 <= n
进行模式匹配以剥离另一个元素)。 K. A. Buhr的方法可以避免此类杂乱无章,这对您很有帮助,如果您只想使用您信任的代码来实现您的操作,并且不安全地强制要排队的类型。
关于haskell - Haskell单例:排版包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46634706/
我最近购买了《C 编程语言》并尝试了 Ex 1-8这是代码 #include #include #include /* * */ int main() { int nl,nt,nb;
早上好!我有一个变量“var”,可能为 0。我检查该变量是否为空,如果不是,我将该变量保存在 php session 中,然后调用另一个页面。在这个新页面中,我检查我创建的 session 是否为空,
我正在努力完成 Learn Python the Hard Way ex.25,但我无法理解某些事情。这是脚本: def break_words(stuff): """this functio
我是一名优秀的程序员,十分优秀!