在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
}
},
1: {
_: ...,
0: {
_: 0,
1: 0
}
}
}
fmap duplicate . duplicate $ x =
{
_: ...,
0: {
_: ...,
1: {
_: 0
}
},
1: {
_: ...,
0: {
_: 0
}
}
}
因此,我们可以看到不匹配是由在实现中删除的键引起的
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级
不完全免费