Add Result.Extra.combineFoldl
combineFoldl
combineFoldl :
(a -> acc -> Result err acc)
-> acc
-> List a
-> Result err acc
combineFoldl f acc list =
case list of
[] ->
Ok acc
head :: tail ->
case f head acc of
Err e ->
Err e
Ok v ->
resultFoldl f v tail
Motivating use case
It's common to fold over a list while keeping track of some state. Doing Result.andThen / Result.map is a List.foldl does not short circuit, whereas this function does.
Why not combineFoldr too?
~~Because it's much harder to implement~~ because you need to traverse the whole list, and at that point might as well use List.foldr
@gampleman opinions?
-
I'd like to see some comparison vs using
List.Extra.stoppableFoldl, since that aims at more or less the same use case. -
I feel like we should figure out some consistent policy for these sorts of functions if they go into List.Extra or Result/Maybe.Extra. My gut feeling here is that this is more of a List.Extra thing, but can't really articulate a coherent argument either way.
-
Can you explain your rationale for the naming? I'm not really seeing much of a commonality between this and the other
Result.Extra.combine*functions...
- It's very similar, but step is
Step awhereas result isResult e x, and the additional type parameter is essential - I'd say this goes in
Result.Extra, and an hypotheticalcombineFoldlwithMaybegoes intoMaybe.Extra - Terrible name, please help come up with a better one
It's very similar, but step is Step a whereas result is Result e x, and the additional type parameter is essential
Yeah but Step (Result e x) is what you could quite easily do. So I guess the question is: is it worth adding this extra abstraction? I think having some code examples written with both would be helpful.
withCombineFoldl : List String -> Result String (Dict String Int)
withCombineFoldl list =
-- Result.Extra.combineFoldl
combineFoldl
(\item acc ->
case parse item of
Err e ->
Err e
Ok ( k, v ) ->
Ok (Dict.insert k v acc)
)
Dict.empty
list
withStoppableFoldl : List String -> Result String (Dict String Int)
withStoppableFoldl list =
List.Extra.stoppableFoldl
(\item acc ->
case acc of
Err e ->
-- This never happens
List.Extra.Stop (Err e)
Ok okAcc ->
case parse item of
Err e ->
List.Extra.Stop (Err e)
Ok ( k, v ) ->
List.Extra.Continue (Ok (Dict.insert k v okAcc))
)
(Ok Dict.empty)
list
It's not terrible to be honest. But it's also not great.
And perhaps like this, which is fairly nice
withCombineFoldl : List String -> Result String (Dict String Int)
withCombineFoldl =
Result.Extra.combineFoldl
(\item acc ->
parse item
|> Result.map (\( k, v ) -> Dict.insert k v acc)
)
Dict.empty
hm OK, I think I'm coming around to this. I'm still not in love with the name. What about foldlUntilError or some such?
That's not a terrible name!