diff --git a/crates/compiler/builtins/roc/List.roc b/crates/compiler/builtins/roc/List.roc index 7ea42e7cb4..44d395bd9d 100644 --- a/crates/compiler/builtins/roc/List.roc +++ b/crates/compiler/builtins/roc/List.roc @@ -38,6 +38,7 @@ interface List min, max, map4, + mapTry, dropFirst, joinMap, any, @@ -858,6 +859,32 @@ split = \elements, userSplitIndex -> { before, others } +## Like [List.map], except the transformation function returns a [Result]. +## If that function ever returns `Err`, [mapTry] immediately returns `Err`. +## If it returns `Ok` for every element, [mapTry] returns `Ok` with the transformed list. +mapTry : List elem, (elem -> Result ok err) -> Result (List ok) err +mapTry = \list, toResult -> + walkTry list [] \state, elem -> + Result.map (toResult elem) \ok -> + List.append state ok + +## This is the same as `iterate` but with Result instead of [Continue, Break]. +## Using `Result` saves a conditional in `mapTry`. +## It might be useful to expose this in userspace? +walkTry : List elem, state, (state, elem -> Result state err) -> Result state err +walkTry = \list, init, func -> + walkTryHelp list init func 0 (List.len list) + +## internal helper +walkTryHelp : List elem, state, (state, elem -> Result state err), Nat, Nat -> Result state err +walkTryHelp = \list, state, f, index, length -> + if index < length then + when f state (List.getUnsafe list index) is + Ok nextState -> walkTryHelp list nextState f (index + 1) length + Err b -> Err b + else + Ok state + ## Primitive for iterating over a List, being able to decide at every element whether to continue iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b] iterate = \list, init, func -> diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 150b661b80..5581da2cc4 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1290,6 +1290,7 @@ define_builtins! { 66 LIST_APPEND_UNSAFE: "appendUnsafe" 67 LIST_SUBLIST_LOWLEVEL: "sublistLowlevel" 68 LIST_CAPACITY: "capacity" + 69 LIST_MAP_TRY: "mapTry" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" // the Result.Result type alias