简体   繁体   English

当非 monadic 值变成 monadic 值时,正确重构 Haskell 代码

[英]Proper refactor of a Haskell code when a non-monadic value becomes a monadic value

Let's say I have this sample code:假设我有这个示例代码:

h = 2
add = \x y -> (x + y)
addH = add h

main = return (fmap addH [1,2])

Running it evaluates to [3,4]运行它评估为 [3,4]


Now, let's say h is not set to "2", but to "Just 2".现在,假设 h 没有设置为“2”,而是“Just 2”。

Question, part 1:问题,第 1 部分:

What is the proper refactor, then, to still return [3,4] in the end?那么,最终仍然返回 [3,4] 的正确重构是什么?

Question, part 2:问题,第 2 部分:

Would a skilled Haskell developper prefer to change the return value to be [Just 3, Just 4]熟练的 Haskell 开发人员是否更愿意将返回值更改为 [Just 3, Just 4]

For example with a refactor like this:例如使用这样的重构:

h = Just 2
add = \x y -> (x + y)
addH = liftM2(add) h . pure         --isn't there a better syntax for that?

main = return (fmap addH [1,2])

More generally, in an existing codebase, how to minimize the refactor impact when a function that used to return 'Num t => [t]' must now return 'Num t => [Maybe t]'?更一般地说,在现有的代码库中,当曾经返回 'Num t => [t]' 的函数现在必须返回 'Num t => [Maybe t]' 时,如何最大限度地减少重构影响?

I would like to point out that in your original code, [] and Maybe are redundant, and that a closer inspection of the meaning of these two functors leads to an interesting discussion.我想指出,在你的原始代码中, []Maybe是多余的,仔细检查这两个函子的含义会导致一个有趣的讨论。 [] (list) represents a success or a failure in the same sense that Maybe does, the main difference being that [] can represent 0 or more values, whereas Maybe is limited to 0 or 1 value. [] (list) 表示成功或失败,与Maybe意义相同,主要区别在于[]可以表示 0 个或多个值,而Maybe仅限于 0 或 1 个值。

This leads to another consideration about the meaning of the Just in Just 2 .这导致了关于Just in Just 2含义的另一个考虑。 What does it mean for 2 to be wrapped in a Just , and what would a Nothing mean?2包裹在JustNothing意思,而NothingNothing意思? To take this one step further, here are five different results that you could opt for, each having a different meaning:为了更进一步,您可以选择以下五种不同的结果,每种结果都有不同的含义:

  1. Just [3, 4] : There was one monolithic computation that dealt with multiple values, and it succeeded. Just [3, 4] :有一个处理多个值的整体计算,并且成功了。
  2. Nothing : There could have been a monolithic value, but there is none. Nothing :可能有一个整体的价值,但没有。
  3. [3, 4] : There was a batch of computations, and these are the values that came out. [3, 4] :有一批计算,这些是出来的值。
  4. [] : There could have been multiple values, but there are none. [] :可能有多个值,但没有。
  5. [Just 3, Just 4] : There was a batch of computations, where each computation could have failed, and all of them succeeded. [Just 3, Just 4] :有一批计算,每个计算都可能失败,但都成功了。

Case 1 : If h represents a monolithic value, and is the unique point of failure in this computation chain, then options 1 and 2 seem like a reasonable choice.情况 1 :如果h代表一个整体值,并且是此计算链中唯一的故障点,则选项 1 和选项 2 似乎是一个合理的选择。 The following code accomplishes this:以下代码实现了这一点:

import Control.Applicative (LiftA2)
h = Just 2
add = (+)
addH = LiftA2 add h . pure
-- Returns Just [3,4] or Nothing
mainFunc = traverse addH [1,2]

Case 2 : If add represents a function that can fail depending on its argument (for example, think of the function hDivBy , which would fail on every 0), then options 3 and 4 seem like a good choice.情况 2 :如果add表示的函数可能会根据其参数而失败(例如,考虑函数hDivBy ,它会在每个 0 处失败),那么选项 3 和 4 似乎是一个不错的选择。

import Data.Maybe (mapMaybe)
hDivBy = \x -> (`div` x) <$> h
--Returns [1]
mainFunc = mapMaybe hDivBy [0, 2]

Case 3 : If you want to keep track of element indexing, then option 5 will give you just that:案例 3 :如果您想跟踪元素索引,那么选项 5 将为您提供:

--Returns [Nothing, Just 2]
mainFunc = fmap hDivBy [0,2]

Notice that this code keeps everything in the Applicative realm, making it more general, amongst other advantages .请注意,此代码将所有内容保留在Applicative领域,使其更通用, 以及其他优点

To acheive Fyodor Soikin result you can try the below one.要获得Fyodor Soikin结果,您可以尝试以下方法。

h = Just 2
add = \x y -> x + y
addH arr = pure fmap <*> (add <$> h) <*> pure arr

main = return $ addH [1, 2]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM