他们在哪里需要?

有一天,我正在谈论函数式编程 - 特别是Haskell和一些Java / Scala家伙,他们问我Monads是什么,它们在哪里是必需的。

那么定义和例子并不那么难 - Maybe MonadIO MonadState Monad等等,所以每个人都至少部分地认为Monads是件好事。

但Monads需要在哪里 - Maybe可以通过Integer设置中的-1 (例如-1String中的""设置来避免。 我写了一个没有State Monad的游戏,虽然不是很好,但是初学者都这么做。

所以我的问题: Monads在哪里必要? - 并且根本无法避免。 (没有混淆 - 我喜欢Monads并使用它们,我只是想知道)。

编辑

我想我必须澄清一下,我认为使用“魔法值”是一个很好的解决方案,但是很多程序员都使用它们,特别是在低级语言中作为C或在SHell脚本中,其中错误往往意味着返回-1

我已经清楚,不使用单子不是一个好主意。 抽象往往是非常有帮助的,但也很复杂,因此很多人都为单子概念而斗争。

我的问题的核心是如果有可能做IO例如,没有monad,仍然是纯粹和功能。 我知道将一种已知的良好解决方案放在一边,以及用火石和火苗点燃火,而不是使用打火机是很乏味和痛苦的。

文章@Antal SZ指的是你可以发明monads的伟大,我掠过它,并且当我有更多时间的时候一定会阅读它。 更多揭示的答案隐藏在@Antal SZ提到的博客文章的评论中,我记得单子之前的时间,这是我在问问题时寻找的东西。


我不认为你需要monads。 当你使用某种功能时,它们只是一种自然出现的模式。 我见过的这个观点的最好解释是Dan Piponi(sigfpe)的优秀博客文章“你可能发明了Monads!(也许你已经拥有了。)”,这个答案的灵感来源于此。

你说你写了一个游戏而不使用状态monad。 它看起来像什么? 很有可能你最终会使用类似openChest :: Player -> Location -> (Item,Player)函数(它会打开一个箱子,可能会用陷阱损坏玩家并返回找到的项目) 。 一旦你需要结合这些,你可以手动( let (item,player') = openChest player loc ; (x,player'') = func2 player' y in ... )或者重新实现状态monad的>>=运算符。

或者,假设我们使用哈希映射/关联数组的语言工作,并且我们不使用单子。 我们需要查找一些项目并与他们合作; 也许我们试图在两个用户之间发送消息。

send username1 username2 = {
    user1 = users[username1]
    user2 = users[username2]
    sendMessage user1 user2 messageBody
}

但是等等,这是行不通的。 username1username2可能会丢失,在这种情况下,它们将是nil-1或其他值,而不是所需的值。 或者,在关联数组中查找键可能会返回Maybe a类型的值,所以这甚至会是一个类型错误。 相反,我们必须写一些类似的东西

send username1 username2 = {
    user1 = users[username1]
    if (user1 == nil) return
    user2 = users[username2]
    if (user2 == nil) return
    sendMessage user1 user2 messageBody
}

或者,使用Maybe

send username1 username2 =
    case users[username1] of
      Just user1 -> case users[username2] of
                      Just user2 -> Just $ sendMessage user1 user2 messageBody
                      Nothing    -> Nothing
      Nothing    -> Nothing

伊克! 这是混乱和过度嵌套。 所以我们定义了一些结合了可能失败的功能的功能。 也许类似

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
f >>= Just x  = f x
f >>= Nothing = Nothing

所以你可以写

send username1 username2 =
  users[username1] >>= $ user1 ->
  users[username2] >>= $ user2 ->
  Just (sendMessage user1 user2 messageBody)

如果你真的不想使用Maybe ,那么你可以实现

f >>= x = if x == nil then nil else f x

同样的原则适用。

不过,真的,我推荐阅读“你可以发明单子!” 这就是我对monads的直觉,并且更详细地解释它。 Monads在使用某些类型时会自然产生。 有时候你会明确表达这种结构,有时候你不这样做,但仅仅因为你不想这样做并不意味着它不存在。 您从不需要使用monad,因为您无需使用该特定结构,但通常这是很自然的事情。 和其他许多事情一样,认识到这种通用模式可以让你编写一些很好的通用代码。

(另外,作为第二个例子中,我使用的节目,请注意,已通过更换抛出的婴儿连同洗澡水Maybe魔法值,只是因为, Maybe是一个单子并不意味着你必须使用它像一个;名单也是单子,就像函数一样(形式为r -> ),但是你不建议摆脱他们!:-))


我可以说“ X是必需的,不可避免的?” X在计算中什么都没有; 重点是什么?

相反,我认为问“ X提供了什么价值?”更有价值。“”。

最基本的答案是,大多数X的计算提供了一个有用的抽象,使得它更容易,更简单,并且不太容易出错将代码放在一起。

好的,但你不需要抽象,对吧? 我的意思是,我可以手动输入一些代码来做同样的事情,对吧? 是的,当然,这只是一堆0和1,所以让我们看看谁可以更快地编写XML解析器,使用Java / Haskell / C还是使用图灵机器。


Re monads:由于monads通常处理有效的计算,所以在编写有效函数时,这种抽象是最有用的。

我对你的“魔法值也许monad”提出质疑。 这种方法为程序员提供了一个非常不同的抽象,并且比实际的Maybe monad更安全,更乏味,更容易出错。 另外,阅读这样的代码,程序员的意图不太清楚。 换句话说,它忽略了真正的单子的全部点,这是提供抽象。

我还想指出,单子对于Haskell来说并不重要:

  • do -notation只是语法糖,可以完全被>>=>>替代,而不会损失任何表现力

  • 他们(和他们的组合者,比如join>>=mapM等)可以用Haskell编写

  • 它们可以用任何支持高阶函数的语言编写,甚至可以用Java语言编写。 所以如果你不得不使用一个没有monad的Lisp,你可以在Lisp中自己实现它们,而不会有太多的麻烦


  • 由于monad类型返回相同类型的答案,因此monad类型的实现可以强制执行并保留语义。 然后,在您的代码中,您可以使用该类型链接操作并让它执行其规则,而不管其包含的类型如何。

    例如,Java 8中的Optional类强制执行规则,即包含的值存在且非空,否则不存在。 只要您使用Optional类,使用或不使用flatMap,就会围绕包含的数据类型包装该规则。 没有人可以作弊或忘记,并添加值= null与present = true。

    因此,在代码之外声明-1将是一个定点值,并且意味着这样 - 那么好,但是您仍然依赖于您自己和其他在代码中工作的人来兑现该语义。 如果一个新人加入并开始使用-1000000来表示同样的事情,那么语义需要在代码之外执行(可能使用引导管?),而不是通过代码机制。

    因此,不必在程序中一致地应用某些规则,您可以相信monad保留该规则(或其他语义) - 在任意类型上。

    通过这种方式,您可以通过在其周围包装语义来扩展类型的功能,而不是为代码库中的每种类型添加“isPresent”。

    众多monadic实用程序类型的存在表明,这种使用语义封装类型的机制是非常有用的技巧。 如果你有自己想添加的语义,你可以通过使用monad模式编写自己的类,然后将字符串或浮点数或整数或任何内容注入到它中。

    但简单的答案是monad是一种很好的方式来将流行或链式容器中的常见类型封装起来,以添加规则和用法,而不必大惊小怪地执行基础类型。

    链接地址: http://www.djcxy.com/p/47667.html

    上一篇: where are they necessary?

    下一篇: Monads with Join() instead of Bind()