我宣布了一个关于应用函子的小组。从我们通常所说的“行动”来看,似乎这样的团体可以使行动被撤销:
\nimport Control.Applicative\n\nclass Alternative f => Undoable f where\n undo :: f a -> f a\nRun Code Online (Sandbox Code Playgroud)\n成为一个团体,为所有人x :: Undoable f => f a,应满足以下法律:
x <|> undo x \xe2\x89\xa1 empty\nundo x <|> x \xe2\x89\xa1 empty\nRun Code Online (Sandbox Code Playgroud)\n一些实例:
\nimport Control.Arrow\nimport Data.Functor.Compose\nimport Data.Functor.Product\nimport Data.Proxy\n\ninstance Undoable Proxy where\n undo _ = Proxy\n\ninstance (Undoable f, Applicative g) => Undoable (Compose f g) where\n undo (Compose x) = Compose (undo x)\n\ninstance (Undoable f, Undoable g) => Undoable (Product …Run Code Online (Sandbox Code Playgroud) 我是有点惊讶,当我读的情况下的源代码Applicative Complex和Monad Complex从GHC Data.Complex模块:
-- | @since 4.9.0.0
instance Applicative Complex where
pure a = a :+ a
f :+ g <*> a :+ b = f a :+ g b
liftA2 f (x :+ y) (a :+ b) = f x a :+ f y b
-- | @since 4.9.0.0
instance Monad Complex where
a :+ b >>= f = realPart (f a) :+ imagPart (f b)
Run Code Online (Sandbox Code Playgroud)
什么......?该Applicative Complex实例似乎将复数视为大小为二的数组.它们看起来更像是箭头操作.他们背后有数学基础吗?是否存在,它们用于什么?
考虑以下数据类型:
data Foo = Halt | Iter Foo
Run Code Online (Sandbox Code Playgroud)
这是 的不动点Maybe。它包含序列、、等的直接极限,其中是注入态射。然而,有一个元素没有被直接极限覆盖,即。VoidMaybe VoidMaybe (Maybe Void)pureFoofix Iter
让我再举一个例子:
data Bar = Bar Bar Bar
Run Code Online (Sandbox Code Playgroud)
这是 的不动点Complex。由于Void、Complex Void、Complex (Complex Void)等都同胚于Void,所以直接极限也Void。然而,Bar有一个元素,即fix (join Bar)。
那么这些“剩余”元素的数学依据是什么?
因为Nothing >>= f = Nothing对于每一个f,以下微不足道的定义适用于mfix:
mfix _ = Nothing
Run Code Online (Sandbox Code Playgroud)
但这没有实际用途,因此我们有以下非全面定义:
mfix f = let a = f (unJust a) in a where
unJust (Just x) = x
unJust Nothing = errorWithoutStackTrace "mfix Maybe: Nothing"
Run Code Online (Sandbox Code Playgroud)
如果这个-clause不会停止,那么mfix f返回会很好.(例如,)
这是不可能的,因为暂停问题无法解决?Nothingletf = Just . (1+)
loop来自的描述Control.Arrow:
循环运算符表示计算,其中输出值作为输入反馈,尽管计算仅发生一次.它以箭头符号表示rec值递归构造的基础.
它的源代码,以及它的实例化(->):
class Arrow a => ArrowLoop a where
loop :: a (b,d) (c,d) -> a b c
instance ArrowLoop (->) where
loop f b = let (c,d) = f (b,d) in c
Run Code Online (Sandbox Code Playgroud)
这让我想起了fix,固定点组合器:
fix :: (a -> a) -> a
fix f = let x = f x in x
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:
loop通道fix?通过标题,我的意思是像Monad m => m (m a).
当 monad 的结构很简单时,我很容易想到这种类型的用法:
[[a]],这是一个多维列表
Maybe (Maybe a),这是一种由两个错误状态邻接的类型
Either e (Either e a),就像上面一样,但是有消息
Monoid m => (m,(m,a)),这是一个 writer monad,有两件事要写
r -> r -> a,这是一个 reader monad,有两个东西可以读取
Identity (Identity a),这仍然是身份 monad
Complex (Complex a),这是一个 2×2 矩阵
但是,如果我考虑以下类型,我的脑海中就会出现混乱:
ReadP (ReadP a)? 为什么它ReadP不是 的实例时会有用Read?
ReadPrec (ReadPrec a)? 像上面那样?
Monad m => Kleisli m a (Kleisli m a b)?
IO (IO a)!?这 …
回忆一下Applicative班级:
class Functor f => Applicative f where
pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(<*>) :: f (a -> b) -> f a -> f b
liftA2 h x y = fmap h x <*> y
(<*>) = liftA2 id
Run Code Online (Sandbox Code Playgroud)
尽管目前尚不清楚如何用(数学)范畴论来表达此类,但当定义以下函数时,情况就会变得清晰:
liftZ2 :: Applicative f => (f a, f b) -> f (a, b)
liftZ2 (x, y) = liftA2 (,) x y …Run Code Online (Sandbox Code Playgroud) 我声明了一类接受值的类型:
class NonEmpty a where
example :: a
Run Code Online (Sandbox Code Playgroud)
而且,我声明了补集类:
import Data.Void
class Empty a where
exampleless :: a -> Void
Run Code Online (Sandbox Code Playgroud)
证明函数空间为空很容易:
instance (NonEmpty a, Empty b) => Empty (a -> b) where
exampleless f = exampleless (f example)
Run Code Online (Sandbox Code Playgroud)
但它的补集又如何呢?Haskell 不允许我同时拥有这些实例:
instance Empty a => NonEmpty (a -> b) where
example = absurd . exampleless
instance NonEmpty b => NonEmpty (a -> b) where
example _ = example
Run Code Online (Sandbox Code Playgroud)
有什么办法可以绕过这个问题吗?
例如,来自std :: deque :: operator = in C++ Reference:
(1)Copy Assignment (const std :: deque&other)
用其他内容的副本替换内容.
如果std :: allocator_traits :: propagate_on_container_copy_assignment()为true,则目标分配器将替换为源分配器的副本.如果目标和源分配器不比较相等,则使用目标(*this)分配器来释放内存,然后在复制元素之前使用其他分配器来分配它.
如果this->get_allocator() == other.get_allocator(),我可以简单地破坏和释放this,如果需要'元素,或分配,如果需要构建元素,或复制分配的元素other来*this如果需要的话.
但如果不是呢?上面的引用是否意味着我不能复制 - 分配元素,所以我必须首先销毁和释放所有元素,使用this->get_allocator(),然后分配和构造元素,使用other.get_allocator()?
但如果是这种情况,我为什么要other.get_allocator()用于分配呢?
以后不会导致一些运行时错误,因为this不会正确释放内存吗?
(2)移动作业 (std :: deque && other)
使用移动语义替换其他内容(即其他数据从其他数据移动到此容器中).其他是后来处于有效但未指定的状态.如果std :: allocator_traits :: propagate_on_container_move_assignment()为true,则目标分配器将替换为源分配器的副本.如果它为假并且源和目标分配器不比较相等,则目标不能获取源存储器的所有权,并且必须单独移动 - 分配每个元素,根据需要使用其自己的分配器分配额外的存储器.在任何情况下,*this中最初存在的所有元素要么被销毁,要么被元素移动赋值替换.
如果this->get_allocator() == other.get_allocator(),这是一项简单的任务.
但如果没有,则上面会出现同样的问题,除非在这种情况下使用移动分配.
在这两种情况下,我还有一个问题.
如果元素既不能复制分配也不能移动分配,是否可以销毁它并从其他元素构建?如果是,我应该使用谁的分配器?
实例MonadPlus IO是唯一的,因为mzero抛出:
Prelude Control.Monad> mzero
*** Exception: user error (mzero)
Run Code Online (Sandbox Code Playgroud)
因此,相应地,这也MonadPlus IO 意味着它也适用于错误。
mzero 如果其他动作不抛出,则显然用作标识元素:
Prelude Control.Monad> mzero `mplus` return 0
0
Prelude Control.Monad> return 0 `mplus` mzero
0
Run Code Online (Sandbox Code Playgroud)
但是当两个动作都抛出时不是这样:
Prelude Control.Monad> fail "Hello, world!" `mplus` mzero
*** Exception: user error (mzero)
Prelude Control.Monad> mzero `mplus` fail "Hello, world!"
*** Exception: user error (Hello, world!)
Run Code Online (Sandbox Code Playgroud)
所以MonadPlus IO不是monoid。
如果MonadPlus在用户意图出错时违反法律,那么它的实际意图是什么?