[英]Proper refactor of a Haskell code when a non-monadic value becomes a monadic value
假设我有这个示例代码:
h = 2
add = \x y -> (x + y)
addH = add h
main = return (fmap addH [1,2])
运行它评估为 [3,4]
现在,假设 h 没有设置为“2”,而是“Just 2”。
问题,第 1 部分:
那么,最终仍然返回 [3,4] 的正确重构是什么?
问题,第 2 部分:
熟练的 Haskell 开发人员是否更愿意将返回值更改为 [Just 3, Just 4]
例如使用这样的重构:
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])
更一般地说,在现有的代码库中,当曾经返回 'Num t => [t]' 的函数现在必须返回 'Num t => [Maybe t]' 时,如何最大限度地减少重构影响?
我想指出,在你的原始代码中, []
和Maybe
是多余的,仔细检查这两个函子的含义会导致一个有趣的讨论。 []
(list) 表示成功或失败,与Maybe
意义相同,主要区别在于[]
可以表示 0 个或多个值,而Maybe
仅限于 0 或 1 个值。
这导致了关于Just
in Just 2
含义的另一个考虑。 将2
包裹在Just
是Nothing
意思,而Nothing
是Nothing
意思? 为了更进一步,您可以选择以下五种不同的结果,每种结果都有不同的含义:
Just [3, 4]
:有一个处理多个值的整体计算,并且成功了。Nothing
:可能有一个整体的价值,但没有。[3, 4]
:有一批计算,这些是出来的值。[]
:可能有多个值,但没有。[Just 3, Just 4]
:有一批计算,每个计算都可能失败,但都成功了。 情况 1 :如果h
代表一个整体值,并且是此计算链中唯一的故障点,则选项 1 和选项 2 似乎是一个合理的选择。 以下代码实现了这一点:
import Control.Applicative (LiftA2)
h = Just 2
add = (+)
addH = LiftA2 add h . pure
-- Returns Just [3,4] or Nothing
mainFunc = traverse addH [1,2]
情况 2 :如果add
表示的函数可能会根据其参数而失败(例如,考虑函数hDivBy
,它会在每个 0 处失败),那么选项 3 和 4 似乎是一个不错的选择。
import Data.Maybe (mapMaybe)
hDivBy = \x -> (`div` x) <$> h
--Returns [1]
mainFunc = mapMaybe hDivBy [0, 2]
案例 3 :如果您想跟踪元素索引,那么选项 5 将为您提供:
--Returns [Nothing, Just 2]
mainFunc = fmap hDivBy [0,2]
请注意,此代码将所有内容保留在Applicative
领域,使其更通用, 以及其他优点。
要获得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.