他们在哪里需要?
有一天,我正在谈论函数式编程 - 特别是Haskell和一些Java / Scala家伙,他们问我Monads是什么,它们在哪里是必需的。
那么定义和例子并不那么难 - Maybe Monad
, IO Monad
, State Monad
等等,所以每个人都至少部分地认为Monads是件好事。
但Monads需要在哪里 - Maybe
可以通过Integer
设置中的-1
(例如-1
或String
中的""
设置来避免。 我写了一个没有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
}
但是等等,这是行不通的。 username1
和username2
可能会丢失,在这种情况下,它们将是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