使用 Join() 而不是 Bind() 的 Monad [英] Monads with Join() instead of Bind()

查看:25
本文介绍了使用 Join() 而不是 Bind() 的 Monad的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Monads 通常按照 returnbind 的顺序来解释.但是,我认为您也可以在 join(和 fmap?)

方面实现 bind

在缺乏一流功能的编程语言中,bind 使用起来极其尴尬.join,另一方面,看起来很容易.

不过,我并不完全确定我了解 join 的工作原理.显然,它具有 [Haskell] 类型

<前>加入 :: Monad m => m (m x) -> m x

对于列表 monad,这是简单而明显的concat.但是对于一般的 monad,从操作上讲,这种方法实际上是做什么的?我看到了它对类型签名的作用,但我想弄清楚如何用 Java 或类似的语言编写这样的东西.

(实际上,这很简单:我不会.因为泛型被破坏了.;-) 但原则上问题仍然存在......)

<小时>

糟糕.之前好像有人问过这个问题:

Monad 连接函数

有人可以使用 returnfmapjoin 勾勒出一些常见 monad 的实现吗?(即,根本没有提到 >>=.)我认为这可能有助于它陷入我愚蠢的大脑......

解决方案

在不深入比喻的情况下,我是否建议将典型的 monad m 解读为产生 a 的策略",因此type m value 是一流的产生价值的策略".不同的计算或外部交互概念需要不同类型的策略,但一般概念需要一些规则结构才能有意义:

  • 如果你已经有了一个值,那么你就有了一个产生一个值的策略(return :: v -> mv),除了产生你拥有的值外,别无其他;
  • 如果您有一个将一种值转换为另一种值的函数,您可以将其提升为策略 (fmap :: (v -> u) -> mv -> mu)只需等待策略交付其价值,然后再进行转换;
  • 如果你有一个策略来产生一个策略来产生一个值,那么你可以构造一个策略来产生一个值 (join :: m (mv) -> mv),它遵循外部策略,直到它产生内部策略,然后遵循该内部策略一直到一个值.

让我们举个例子:叶标记二叉树...

data Tree v = Leaf v |节点(树 v)(树 v)

...表示通过抛硬币来生产东西的策略.如果策略是Leaf v,那就是你的v;如果策略是 Node ht,则抛硬币并继续策略 h 如果硬币显示正面",t 如果它是反面"".

instance Monad Tree where返回 = 叶子

产生策略的策略是一棵带有树标记叶子的树:代替每个这样的叶子,我们可以直接嫁接到标记它的树中......

 join (叶树) = 树加入(节点h t)=节点(加入h)(加入t)

...当然我们还有 fmap ,它只是重新标记叶子.

instance Functor Tree wherefmap f (叶 x) = 叶 (f x)fmap f (节点 h t) = 节点 (fmap f h) (fmap f t)

这是一个产生一个策略来产生一个Int的策略.

抛硬币:如果是正面",再抛硬币来决定两种策略(产生,分别是抛硬币产生 0 或产生 1"或产生 2");如果它是尾巴",则产生三分之一(抛硬币产生 3 或抛硬币产生 4 或 5").

显然join是为了制定一个产生Int的策略.

我们利用的是这样一个事实,即产生价值的策略"本身可以被视为一种价值.在 Haskell 中,策略作为值的嵌入是无声的,但在英语中,我使用引号来区分使用策略和只是谈论它.join 操作符表达了以某种方式产生然后遵循策略"的策略,或者如果你告诉一个策略,你可以使用它".

(元.我不确定这种策略"方法是否是考虑 monad 和值/计算区别的合适通用方式,或者它是否只是另一个糟糕的比喻.我确实发现叶子标记的树状输入一个有用的直觉来源,这也许并不奇怪,因为它们是免费 monad,结构刚好足以成为 monad,但仅此而已.)

PS绑定"的类型

(>>=) :: m v ->(v -> m w) ->兆瓦

说如果你有一个产生 v 的策略,并且对于每个 va 后续策略产生一个 w,那么你有一个策略来产生一个w".我们如何通过 join 来捕捉这一点?

mv >>= v2mw = join (fmap v2mw mv)

我们可以通过 v2mw 重新标记我们的 v 生成策略,生成 w 代替每个 v 值-随之而来的生产策略—准备加入

Monads are usually explained in turns of return and bind. However, I gather you can also implement bind in terms of join (and fmap?)

In programming languages lacking first-class functions, bind is excruciatingly awkward to use. join, on the other hand, looks quite easy.

I'm not completely sure I understand how join works, however. Obviously, it has the [Haskell] type

join :: Monad m => m (m x) -> m x

For the list monad, this is trivially and obviously concat. But for a general monad, what, operationally, does this method actually do? I see what it does to the type signatures, but I'm trying to figure out how I'd write something like this in, say, Java or similar.

(Actually, that's easy: I wouldn't. Because generics is broken. ;-) But in principle the question still stands...)


Oops. It looks like this has been asked before:

Monad join function

Could somebody sketch out some implementations of common monads using return, fmap and join? (I.e., not mentioning >>= at all.) I think perhaps that might help it to sink in to my dumb brain...

解决方案

Without plumbing the depths of metaphor, might I suggest to read a typical monad m as "strategy to produce a", so the type m value is a first class "strategy to produce a value". Different notions of computation or external interaction require different types of strategy, but the general notion requires some regular structure to make sense:

  • if you already have a value, then you have a strategy to produce a value (return :: v -> m v) consisting of nothing other than producing the value that you have;
  • if you have a function which transforms one sort of value into another, you can lift it to strategies (fmap :: (v -> u) -> m v -> m u) just by waiting for the strategy to deliver its value, then transforming it;
  • if you have a strategy to produce a strategy to produce a value, then you can construct a strategy to produce a value (join :: m (m v) -> m v) which follows the outer strategy until it produces the inner strategy, then follows that inner strategy all the way to a value.

Let's have an example: leaf-labelled binary trees...

data Tree v = Leaf v | Node (Tree v) (Tree v)

...represent strategies to produce stuff by tossing a coin. If the strategy is Leaf v, there's your v; if the strategy is Node h t, you toss a coin and continue by strategy h if the coin shows "heads", t if it's "tails".

instance Monad Tree where
  return = Leaf

A strategy-producing strategy is a tree with tree-labelled leaves: in place of each such leaf, we can just graft in the tree which labels it...

  join (Leaf tree) = tree
  join (Node h t)  = Node (join h) (join t)

...and of course we have fmap which just relabels leaves.

instance Functor Tree where
  fmap f (Leaf x)    = Leaf (f x)
  fmap f (Node h t)  = Node (fmap f h) (fmap f t)

Here's an strategy to produce a strategy to produce an Int.

Toss a coin: if it's "heads", toss another coin to decide between two strategies (producing, respectively, "toss a coin for producing 0 or producing 1" or "produce 2"); if it's "tails" produce a third ("toss a coin for producing 3 or tossing a coin for 4 or 5").

That clearly joins up to make a strategy producing an Int.

What we're making use of is the fact that a "strategy to produce a value" can itself be seen as a value. In Haskell, the embedding of strategies as values is silent, but in English, I use quotation marks to distinguish using a strategy from just talking about it. The join operator expresses the strategy "somehow produce then follow a strategy", or "if you are told a strategy, you may then use it".

(Meta. I'm not sure whether this "strategy" approach is a suitably generic way to think about monads and the value/computation distinction, or whether it's just another crummy metaphor. I do find leaf-labelled tree-like types a useful source of intuition, which is perhaps not a surprise as they're the free monads, with just enough structure to be monads at all, but no more.)

PS The type of "bind"

(>>=) :: m v -> (v -> m w) -> m w

says "if you have a strategy to produce a v, and for each v a follow-on strategy to produce a w, then you have a strategy to produce a w". How can we capture that in terms of join?

mv >>= v2mw = join (fmap v2mw mv)

We can relabel our v-producing strategy by v2mw, producing instead of each v value the w-producing strategy which follows on from it — ready to join!

这篇关于使用 Join() 而不是 Bind() 的 Monad的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆