Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Haskell中使用涉及CmpNat和Singleton的证明_Haskell_Constraints_Type Families_Type Level Computation_Singleton Type - Fatal编程技术网

在Haskell中使用涉及CmpNat和Singleton的证明

在Haskell中使用涉及CmpNat和Singleton的证明,haskell,constraints,type-families,type-level-computation,singleton-type,Haskell,Constraints,Type Families,Type Level Computation,Singleton Type,我正在尝试创建一些函数来处理以下类型。以下代码使用GHC-8.4.1中的和库: {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-}

我正在尝试创建一些函数来处理以下类型。以下代码使用GHC-8.4.1中的和库:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE UndecidableInstances #-}

import Data.Constraint ((:-))
import Data.Singletons (sing)
import Data.Singletons.Prelude (Sing(SEQ, SGT, SLT), (%+), sCompare)
import Data.Singletons.Prelude.Num (PNum((+)))
import Data.Singletons.TypeLits (SNat)
import GHC.TypeLits (CmpNat, Nat)

data Foo where
  Foo
    :: forall (index :: Nat) (len :: Nat).
       (CmpNat index len ~ 'LT)
    => SNat index
    -> SNat len
    -> Foo
这是一个包含长度和索引的GADT。保证索引小于长度

编写函数来创建
Foo
,非常简单:

createFoo :: Foo
createFoo = Foo (sing :: SNat 0) (sing :: SNat 1)
但是,我在编写一个函数时遇到了问题,该函数在使
索引保持不变的情况下增加
len

incrementLength :: Foo -> Foo
incrementLength (Foo index len) = Foo index (len %+ (sing :: SNat 1))
此操作失败,出现以下错误:

file.hs:34:34: error:
    • Could not deduce: CmpNat index (len GHC.TypeNats.+ 1) ~ 'LT
        arising from a use of ‘Foo’
      from the context: CmpNat index len ~ 'LT
        bound by a pattern with constructor:
                   Foo :: forall (index :: Nat) (len :: Nat).
                          (CmpNat index len ~ 'LT) =>
                          SNat index -> SNat len -> Foo,
                 in an equation for ‘incrementLength’
        at what5.hs:34:17-29
    • In the expression: Foo index (len %+ (sing :: SNat 1))
      In an equation for ‘incrementLength’:
          incrementLength (Foo index len)
            = Foo index (len %+ (sing :: SNat 1))
    • Relevant bindings include
        len :: SNat len (bound at what5.hs:34:27)
        index :: SNat index (bound at what5.hs:34:21)
   |
34 | incrementLength (Foo index len) = Foo index (len %+ (sing :: SNat 1))
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这是有意义的,因为编译器知道
CmpNat索引len~'LT
(根据Foo的定义),但不知道
CmpNat索引(len+1)~'LT

有没有办法让这样的东西起作用

可以使用
sCompare
执行以下操作:

incrementLength :: Foo -> Foo
incrementLength (Foo index len) =
  case sCompare index (len %+ (sing :: SNat 1)) of
    SLT -> Foo index (len %+ (sing :: SNat 1))
    SEQ -> error "not eq"
    SGT -> error "not gt"
然而,不幸的是,我不得不为
SEQ
SGT
编写案例,而我知道它们永远不会匹配

此外,我认为可以创建如下类型:

plusOneLTProof :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
plusOneLTProof = undefined
file.hs:40:8: error:
    • Couldn't match type ‘CmpNat n0 m0’ with ‘CmpNat n m’
      Expected type: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
        Actual type: (CmpNat n0 m0 ~ 'LT) :- (CmpNat n0 (m0 + 1) ~ 'LT)
      NB: ‘CmpNat’ is a non-injective type family
      The type variables ‘n0’, ‘m0’ are ambiguous
    • In the ambiguity check for ‘bar’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        bar :: (CmpNat n m ~  'LT) :- (CmpNat n (m + 1) ~  'LT)
   |
40 | bar :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
incrementLength :: Foo -> Foo
incrementLength (Foo index len) =
  case plusOneLTProof index len of
    Sub Dict -> Foo index (len %+ (sing :: SNat 1))

plusOneLTProof :: forall n m. SNat n -> SNat m -> (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
plusOneLTProof SNat SNat = Sub axiom
  where
    axiom :: CmpNat n m ~ 'LT => Dict (CmpNat n (m + 1) ~ 'LT)
    axiom = unsafeCoerce (Dict :: Dict (a ~ a))
但是,这会产生如下错误:

plusOneLTProof :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
plusOneLTProof = undefined
file.hs:40:8: error:
    • Couldn't match type ‘CmpNat n0 m0’ with ‘CmpNat n m’
      Expected type: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
        Actual type: (CmpNat n0 m0 ~ 'LT) :- (CmpNat n0 (m0 + 1) ~ 'LT)
      NB: ‘CmpNat’ is a non-injective type family
      The type variables ‘n0’, ‘m0’ are ambiguous
    • In the ambiguity check for ‘bar’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        bar :: (CmpNat n m ~  'LT) :- (CmpNat n (m + 1) ~  'LT)
   |
40 | bar :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
incrementLength :: Foo -> Foo
incrementLength (Foo index len) =
  case plusOneLTProof index len of
    Sub Dict -> Foo index (len %+ (sing :: SNat 1))

plusOneLTProof :: forall n m. SNat n -> SNat m -> (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
plusOneLTProof SNat SNat = Sub axiom
  where
    axiom :: CmpNat n m ~ 'LT => Dict (CmpNat n (m + 1) ~ 'LT)
    axiom = unsafeCoerce (Dict :: Dict (a ~ a))
我想这是有道理的,因为CmpNat是非内射的。但是,我知道这是真的,所以我希望能够编写这个函数


我想回答以下两个问题:

  • 有没有一种方法可以编写
    incrementLength
    ,只需在
    SLT
    上进行匹配?我可以修改
    Foo
    的定义,使之更简单

  • 有没有一种方法可以编写
    plusOnline
    ,或者至少是类似的东西


  • 更新:我最终根据李耀霞的建议编写了
    plusInEltProof
    incrementLength
    ,如下所示:

    plusOneLTProof :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
    plusOneLTProof = undefined
    
    file.hs:40:8: error:
        • Couldn't match type ‘CmpNat n0 m0’ with ‘CmpNat n m’
          Expected type: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
            Actual type: (CmpNat n0 m0 ~ 'LT) :- (CmpNat n0 (m0 + 1) ~ 'LT)
          NB: ‘CmpNat’ is a non-injective type family
          The type variables ‘n0’, ‘m0’ are ambiguous
        • In the ambiguity check for ‘bar’
          To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
          In the type signature:
            bar :: (CmpNat n m ~  'LT) :- (CmpNat n (m + 1) ~  'LT)
       |
    40 | bar :: (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
       |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    incrementLength :: Foo -> Foo
    incrementLength (Foo index len) =
      case plusOneLTProof index len of
        Sub Dict -> Foo index (len %+ (sing :: SNat 1))
    
    plusOneLTProof :: forall n m. SNat n -> SNat m -> (CmpNat n m ~ 'LT) :- (CmpNat n (m + 1) ~ 'LT)
    plusOneLTProof SNat SNat = Sub axiom
      where
        axiom :: CmpNat n m ~ 'LT => Dict (CmpNat n (m + 1) ~ 'LT)
        axiom = unsafeCoerce (Dict :: Dict (a ~ a))
    

    这要求您将两个
    SNat
    s传递给
    plusOneLTProof
    ,但它不需要
    AllowAmbiguousTypes
    编译器拒绝
    plusOneLTProof
    ,因为它的类型不明确。我们可以使用扩展名
    AllowAmbiguousTypes
    禁用该约束。我建议将其与
    ExplicitForall
    (这是由
    ScopedTypeVariables
    暗示的,我们肯定需要它,或者
    RankNTypes
    )。这是为了定义它。类型不明确的定义可与
    TypeApplications
    一起使用

    然而,GHC仍然无法对自然进行推理,因此我们无法定义
    plusOnly=Sub-Dict
    ,更不用说
    incrementLength
    ,这是不安全的

    但是我们仍然可以用
    不安全的力量
    凭空创造证据。这实际上就是约束中模块的实现方式;不幸的是,它目前没有包含关于
    CmpNat
    的任何事实。强制生效是因为类型equalities中没有运行时内容。即使运行时值看起来不错,因此断言不一致的事实仍然可能导致程序出错

    plusOneLTProof :: forall n m. (CmpNat n m ~ 'LT) :- (CmpNat n (m+1) ~ 'LT)
    plusOneLTProof = Sub axiom
      where
        axiom :: (CmpNat n m ~ 'LT) => Dict (CmpNat n (m+1) ~ 'LT)
        axiom = unsafeCoerce (Dict :: Dict (a ~ a))
    
    为了使用证明,我们将其专门化(使用
    TypeApplications
    )并在其上进行模式匹配,以便在上下文中引入RHS,检查LHS是否成立

    incrementLength :: Foo -> Foo
    incrementLength (Foo (n :: SNat n) (m :: SNat m)) =
      case plusOneLTProof @n @m of
        Sub Dict -> Foo n (m %+ (sing :: SNat 1))