代码之家  ›  专栏  ›  技术社区  ›  Asad Saeeduddin

为什么我找不到任何违反法律的行为?

  •  0
  • Asad Saeeduddin  · 技术社区  · 4 年前

    在Twitter上,chrispenner提出了一个有趣的问题 comonad instance

    data MapF f k a = f a :< Map k (f a)
      deriving (Show, Eq, Functor, Foldable, Traversable)
    
    instance (Ord k, Comonad f) => Comonad (MapF f k)
      where
      extract (d :< _) = extract d
    
      duplicate :: forall a. MapF f k a -> MapF f k (MapF f k a)
      duplicate (d :< m) = extend (:< m) d :< M.mapWithKey go m
        where
        go :: k -> f a -> f (MapF f k a)
        go k = extend (:< M.delete k m)
    

    我对这个comonad实例有点怀疑,所以我试着用 hedgehog-classes f ;the Identity 科摩纳德:

    genMap :: Gen a -> Gen (Map Int a)
    genMap g = G.map (R.linear 0 10) ((,) <$> G.int (R.linear 0 10) <*> f g)
    
    genMapF :: (Gen a -> Gen (f a)) -> Gen a -> Gen (MapF f Int a)
    genMapF f g = (:<) <$> f g <*> genMap g
    
    genId :: Gen a -> Gen (Identity a)
    genId g = Identity <$> g
    
    main :: IO Bool
    main = do
      lawsCheck $ comonadLaws $ genMapF genId
    

        ━━━ Context ━━━
        When testing the Double Duplicate law(†), for the Comonad typeclass, the following test failed:
    
        duplicate . duplicate $ x ≡ fmap duplicate . duplicate $ x, where
          x = Identity 0 :< fromList [(0,Identity 0),(1,Identity 0)]
    
    
        The reduced test is:
          Identity (Identity (Identity 0 :< fromList [(0,Identity 0),(1,Identity 0)]) :< fromList [(0,Identity (Identity 0 :< fromList [(1,Identity 0)])),(1,Identity (Identity 0 :< fromList [(0,Identity 0)]))]) :< fromList [(0,Identity (Identity (Identity 0 :< fromList [(1,Identity 0)]) :< fromList [(1,Identity (Identity 0 :< fromList [(0,Identity 0)]))])),(1,Identity (Identity (Identity 0 :< fromList [(0,Identity 0)]) :< fromList [(0,Identity (Identity 0 :< fromList [(1,Identity 0)]))]))]
          ≡
          Identity (Identity (Identity 0 :< fromList [(0,Identity 0),(1,Identity 0)]) :< fromList [(0,Identity (Identity 0 :< fromList [(1,Identity 0)])),(1,Identity (Identity 0 :< fromList [(0,Identity 0)]))]) :< fromList [(0,Identity (Identity (Identity 0 :< fromList [(1,Identity 0)]) :< fromList [(1,Identity (Identity 0 :< fromList []))])),(1,Identity (Identity (Identity 0 :< fromList [(0,Identity 0)]) :< fromList [(0,Identity (Identity 0 :< fromList []))]))]
    
        The law in question:
          (†) Double Duplicate Law: duplicate . duplicate ≡ fmap duplicate . duplicate
    
        ━━━━━━━━━━━━━━━
    

    不幸的是,这个反例很难解析,即使对于非常简单的输入也是如此。

    f级 身份 科摩纳德。而且对于任何 f级 , Map f k a 可以分解成 Compose (Map Identity k a) f

    这样我们就可以摆脱 f级 身份 贯穿始终:

    data MapF' k a = a ::< Map k a
      deriving (Show, Eq, Functor)
    
    instance Ord k => Comonad (MapF' k)
      where
      extract (a ::< _) = a
      duplicate (d ::< m) = (d ::< m) ::< M.mapWithKey (\k v -> v ::< M.delete k m) m
    
    genMapF' :: Gen a -> Gen (MapF' Int a)
    genMapF' g = (::<) <$> g <*> genMap g
    
    main :: IO Bool
    main = do
      -- ...
      lawsCheck $ comonadLaws $ genMapF'
    

        ━━━ Context ━━━
        When testing the Double Duplicate law(†), for the Comonad typeclass, the following test failed:
    
        duplicate . duplicate $ x ≡ fmap duplicate . duplicate $ x, where
          x = 0 ::< fromList [(0,0),(1,0)]
    
    
        The reduced test is:
          ((0 ::< fromList [(0,0),(1,0)]) ::< fromList [(0,0 ::< fromList [(1,0)]),(1,0 ::< fromList [(0,0)])]) ::< fromList [(0,(0 ::< fromList [(1,0)]) ::< fromList [(1,0 ::< fromList [(0,0)])]),(1,(0 ::< fromList [(0,0)]) ::< fromList [(0,0 ::< fromList [(1,0)])])]
          ≡
          ((0 ::< fromList [(0,0),(1,0)]) ::< fromList [(0,0 ::< fromList [(1,0)]),(1,0 ::< fromList [(0,0)])]) ::< fromList [(0,(0 ::< fromList [(1,0)]) ::< fromList [(1,0 ::< fromList [])]),(1,(0 ::< fromList [(0,0)]) ::< fromList [(0,0 ::< fromList [])])]
    
        The law in question:
          (†) Double Duplicate Law: duplicate . duplicate ≡ fmap duplicate . duplicate
    
        ━━━━━━━━━━━━━━━
    

    用一些虚构的语法 show MapF' s、 反例更容易理解:

    x =
    { _: 0, 0: 0, 1: 0 }
    
    duplicate . duplicate $ x =
    {
      _: ...,
      0: {
        _: ...,
        1: {
          _: 0,
          0: 0  # notice the extra field here 
        }
      },
      1: {
        _: ...,
        0: {
          _: 0,
          1: 0 # ditto
        }
      }
    }
    
    
    fmap duplicate . duplicate $ x =
    {
      _: ...,
      0: {
        _: ...,
        1: {
          _: 0 # it's not present here
        }
      },
      1: {
        _: ...,
        0: {
          _: 0 # ditto
        }
      }
    }
    

    因此,我们可以看到不匹配是由在实现中删除的键引起的 duplicate


    instance Ord k => Comonad (MapF' k)
      where
      extract (a ::< _) = a
    
    {-
      -- Old implementation
      duplicate (d ::< m) = (d ::< m) ::< M.mapWithKey (\k v -> v ::< M.delete k m) m
    -}
    
      -- New implementation
      duplicate (d ::< m) = (d ::< m) ::< fmap (::< m) m
    

    令我惊讶的是,它通过了所有测试(包括复制/复制测试):

    Comonad: Extend/Extract Identity    ✓ <interactive> passed 100 tests.
    Comonad: Extract/Extend    ✓ <interactive> passed 100 tests.
    Comonad: Extend/Extend    ✓ <interactive> passed 100 tests.
    Comonad: Extract Right Identity    ✓ <interactive> passed 100 tests.
    Comonad: Extract Left Identity    ✓ <interactive> passed 100 tests.
    Comonad: Cokleisli Associativity    ✓ <interactive> passed 100 tests.
    Comonad: Extract/Duplicate Identity    ✓ <interactive> passed 100 tests.
    Comonad: Fmap Extract/Duplicate Identity    ✓ <interactive> passed 100 tests.
    Comonad: Double Duplication    ✓ <interactive> passed 100 tests.
    Comonad: Extend/Fmap . Duplicate Identity    ✓ <interactive> passed 100 tests.
    Comonad: Duplicate/Extend id Identity    ✓ <interactive> passed 100 tests.
    Comonad: Fmap/Extend Extract    ✓ <interactive> passed 100 tests.
    Comonad: Fmap/LiftW Isomorphism    ✓ <interactive> passed 100 tests.
    

    Map fmap 结束,即 Functor . 因此,对于这种类型,一个更合适的名称可能是 NotQuiteCofree :

    --   Cofree         f a = a :< f (Cofree f a)
    data NotQuiteCofree f a = a :< f a
    
    instance Functor f => Comonad (NotQuiteCofree f)
      where
      duplicate (a :< m) = (a :< m) :< fmap (:< m) m -- Exactly what we had before
      extract (a :< _) = a
    

    f级 s(包括 Map k s) 地址:

    genNQC :: (Gen a -> Gen (f a)) -> Gen a -> Gen (NotQuiteCofree f a)
    genNQC f g = (:<) <$> g <*> f g
    
    -- NotQuiteCofree Identity ~ Pair
    genId :: Gen a -> Gen (Identity a)
    genId g = Identity <$> g
    
    -- NotQuiteCofree (Const x) ~ (,) x
    genConst :: Gen a -> Gen (Const Int a)
    genConst g = Const <$> G.int (R.linear 0 10)
    
    -- NotQuiteCofree [] ~ NonEmpty
    genList :: Gen a -> Gen [a]
    genList g = G.list (R.linear 0 10) g
    
    -- NotQuiteCofree (Map k) ~ ???
    genMap :: Gen a -> Gen (Map Int a)
    genMap g = G.map (R.linear 0 10) ((,) <$> (G.int $ R.linear 0 10) <*> g)
    
    main :: IO Bool
    main = do
      lawsCheck $ comonadLaws $ genNQC genId
      lawsCheck $ comonadLaws $ genNQC genConst
      lawsCheck $ comonadLaws $ genNQC genList
      lawsCheck $ comonadLaws $ genNQC genMap
    


    事实上 不完全免费 生成 据推测 所有这些函子的有效余元对我来说有点重要。 NotQuiteCofree f a 很明显不是同构的 Cofree f a .

    函子 s到 Comonad s必须是唯一的直到唯一同构,因为它定义为附加的右半部分。 明显不同于 Cofree f级 为了什么 NotQuiteCofree f 科摩纳德。

    现在是实际问题。事实上,我没有发现任何失败是我编写生成器时的错误造成的,或者是hedgehog类中的错误,或者只是运气不好?如果没有,以及 NotQuiteCofree {Identity | Const x | [] | Map k} 真的是科摩纳德,你能想出其他的吗 f级 不完全免费

    或者,真的是这样吗 f级 不完全免费

    0 回复  |  直到 4 年前
        1
  •  4
  •   Reed Mullanix    4 年前

    Set ,但我想我们应该可以一概而论。然而,这个证明使用了这样一个事实:我们可以在

    这是Agda的证据,使用 https://github.com/agda/agda-categories

    {-# OPTIONS --without-K --safe #-}
    
    module Categories.Comonad.Morphism where
    
    open import Level
    open import Data.Product hiding (_×_)
    
    open import Categories.Category.Core
    
    open import Categories.Comonad
    open import Categories.Functor renaming (id to Id)
    open import Categories.NaturalTransformation hiding (id)
    open import Categories.Category.Cartesian
    open import Categories.Category.Product
    import Categories.Morphism.Reasoning as MR
    open import Relation.Binary.PropositionalEquality
    
    module Cofreeish-F {o ℓ e} (𝒞 : Category o ℓ e) (𝒞-Products : BinaryProducts 𝒞) where
      open BinaryProducts 𝒞-Products hiding (_⁂_)
      open Category 𝒞
      open MR 𝒞
      open HomReasoning
    
      Cofreeish : (F : Endofunctor 𝒞) → Endofunctor 𝒞
      Cofreeish F = record
        { F₀ = λ X → X × F₀ X
        ; F₁ = λ f → ⟨ f ∘ π₁ , F₁ f ∘ π₂ ⟩
        ; identity = λ {A} → unique id-comm (id-comm ○ ∘-resp-≈ˡ (⟺ identity)) ; homomorphism = λ {X} {Y} {Z} {f} {g} →
          unique (pullˡ project₁ ○ pullʳ project₁ ○ ⟺ assoc) (pullˡ project₂ ○ pullʳ project₂ ○ pullˡ (⟺ homomorphism))
        ; F-resp-≈ = λ eq → unique (project₁ ○ ∘-resp-≈ˡ (⟺ eq)) (project₂ ○ ∘-resp-≈ˡ (F-resp-≈ (⟺ eq)))
        }
        where
          open Functor F
    
      Strong : (F : Endofunctor 𝒞) → Set (o ⊔ ℓ ⊔ e)
      Strong F = NaturalTransformation (-×- ∘F (F ⁂ Id)) (F ∘F -×-)
    
    
    open import Categories.Category.Instance.Sets
    open import Categories.Category.Monoidal.Instance.Sets
    
    module _ (c : Level) where
      open Cofreeish-F (Sets c) Product.Sets-has-all
      open Category (Sets c)
      open MR (Sets c)
      open BinaryProducts {𝒞 = Sets c} Product.Sets-has-all
      open ≡-Reasoning
    
      strength : ∀ (F : Endofunctor (Sets c)) → Strong F
      strength F = ntHelper record
        { η = λ X (fa , b) → F.F₁ (_, b) fa
        ; commute = λ (f , g) {(fa , b)} → trans (sym F.homomorphism) F.homomorphism
        }
        where
          module F = Functor F
    
      Cofreeish-Comonad : (F : Endofunctor (Sets c)) → Comonad (Sets c)
      Cofreeish-Comonad F = record
        { F = Cofreeish F
        ; ε = ntHelper record
          { η = λ X → π₁
          ; commute = λ f → refl
          }
        ; δ = ntHelper record
          { η = λ X → ⟨ id , F-strength.η _ ∘ Δ ∘ π₂ ⟩
          ; commute = λ f → cong₂ _,_ refl (trans (sym F.homomorphism) F.homomorphism)
          }
        ; assoc = δ-assoc
        ; sym-assoc = sym δ-assoc
        ; identityˡ = ε-identityˡ
        ; identityʳ = ε-identityʳ
        }
        where
          module F = Functor F
          module F-strength = NaturalTransformation (strength F)
    
          δ : ∀ X → X × F.F₀ X → (X × F.F₀ X) × F.F₀ (X × F.F₀ X)
          δ _ = ⟨ id , F-strength.η _ ∘ Δ ∘ π₂ ⟩
    
          ε : ∀ X → X × F.F₀ X → X
          ε _ = π₁
    
          δ-assoc : ∀ {X} → δ (X × F.F₀ X) ∘ δ X ≈ ⟨ id , F.F₁ (δ X) ∘ π₂ ⟩ ∘ δ X
          δ-assoc {X} {(x , fx)} = cong₂ _,_ refl (trans (sym F.homomorphism) F.homomorphism)
    
          ε-identityˡ : ∀ {X} → ⟨ ε X ∘ π₁ , F.F₁ (ε X) ∘ π₂ ⟩ ∘ δ X ≈ id
          ε-identityˡ {X} {(x , fx)} = cong₂ _,_ refl (trans (sym F.homomorphism) F.identity)
    
          ε-identityʳ : ∀ {X} → ε (X × F.F₀ X) ∘ δ X ≈ id
          ε-identityʳ {X} {(x , fx)} = refl
    
        2
  •  4
  •   Asad Saeeduddin    4 年前

    NotQuiteCofree 明显不同于 Cofree ,所以我们希望至少有一些 f NotQuiteCofree f

    1. 不完全免费 是每个函子的余元 f级
    2. 不完全免费 不是一个免费的comonad

    “生成一个cofree comonad(从任何函子)”是比“生成一个comonad”更严格的要求。