[英]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.