mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 23:37:56 +03:00
Merge pull request #3568 from rtfeldman/list-maptry
This commit is contained in:
commit
d5f36a6780
@ -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 that `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 ->
|
||||
|
@ -110,6 +110,10 @@ flags! {
|
||||
/// Print to stderr when a runtime error function is generated.
|
||||
ROC_PRINT_RUNTIME_ERROR_GEN
|
||||
|
||||
/// Generate a layout error when an unbound type variable is found, rather than generating the
|
||||
/// void layout.
|
||||
ROC_NO_UNBOUND_LAYOUT
|
||||
|
||||
// ===LLVM Gen===
|
||||
|
||||
/// Prints LLVM function verification output.
|
||||
|
@ -31,6 +31,11 @@ impl Wasm32Sized for () {
|
||||
const ALIGN_OF_WASM: usize = 0;
|
||||
}
|
||||
|
||||
impl Wasm32Sized for std::convert::Infallible {
|
||||
const SIZE_OF_WASM: usize = 0;
|
||||
const ALIGN_OF_WASM: usize = 0;
|
||||
}
|
||||
|
||||
impl Wasm32Sized for RocStr {
|
||||
const SIZE_OF_WASM: usize = 12;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
@ -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
|
||||
|
@ -1227,12 +1227,21 @@ impl<'a> Layout<'a> {
|
||||
|
||||
fn new_help<'b>(
|
||||
env: &mut Env<'a, 'b>,
|
||||
var: Variable,
|
||||
_var: Variable,
|
||||
content: Content,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
use roc_types::subs::Content::*;
|
||||
match content {
|
||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||
FlexVar(_) | RigidVar(_) => {
|
||||
roc_debug_flags::dbg_do!(roc_debug_flags::ROC_NO_UNBOUND_LAYOUT, {
|
||||
return Err(LayoutProblem::UnresolvedTypeVar(_var));
|
||||
});
|
||||
|
||||
// If we encounter an unbound type var (e.g. `*` or `a`)
|
||||
// then it's zero-sized; In the future we may drop this argument
|
||||
// completely, but for now we represent it with the empty tag union
|
||||
Ok(Layout::VOID)
|
||||
}
|
||||
FlexAbleVar(_, _) | RigidAbleVar(_, _) => todo_abilities!("Not reachable yet"),
|
||||
RecursionVar { structure, .. } => {
|
||||
let structure_content = env.subs.get_content_without_compacting(structure);
|
||||
|
@ -1,9 +1,6 @@
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::expect_runtime_error_panic;
|
||||
|
||||
// #[cfg(feature = "gen-dev")]
|
||||
// use crate::helpers::dev::assert_evals_to;
|
||||
|
||||
@ -16,7 +13,9 @@ use crate::helpers::with_larger_debug_stack;
|
||||
#[allow(unused_imports)]
|
||||
use indoc::indoc;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::{RocList, RocStr};
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
@ -257,6 +256,59 @@ fn list_sublist() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_try_ok() {
|
||||
assert_evals_to!(
|
||||
// No transformation
|
||||
r#"
|
||||
List.mapTry [1, 2, 3] \elem -> Ok elem
|
||||
"#,
|
||||
RocResult::ok(RocList::<i64>::from_slice(&[1, 2, 3])),
|
||||
RocResult<RocList<i64>, ()>
|
||||
);
|
||||
assert_evals_to!(
|
||||
// Transformation
|
||||
r#"
|
||||
List.mapTry [1, 2, 3] \num ->
|
||||
str = Num.toStr (num * 2)
|
||||
|
||||
Ok "\(str)!"
|
||||
"#,
|
||||
RocResult::ok(RocList::<RocStr>::from_slice(&[
|
||||
RocStr::from("2!"),
|
||||
RocStr::from("4!"),
|
||||
RocStr::from("6!"),
|
||||
])),
|
||||
RocResult<RocList<RocStr>, ()>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_try_err() {
|
||||
assert_evals_to!(
|
||||
r#"
|
||||
List.mapTry [1, 2, 3] \_ -> Err -1
|
||||
"#,
|
||||
RocResult::err(-1),
|
||||
RocResult<RocList<Infallible>, i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
// If any element returns Err, the whole thing returns Err
|
||||
r#"
|
||||
List.mapTry [1, 2, 3] \num ->
|
||||
if num > 2 then
|
||||
Err -1
|
||||
else
|
||||
Ok num
|
||||
"#,
|
||||
RocResult::err(-1),
|
||||
RocResult<RocList<i64>, i64>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_split() {
|
||||
@ -2670,18 +2722,8 @@ fn list_any() {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)]
|
||||
fn list_any_empty_with_unknown_element_type() {
|
||||
// Segfaults with invalid memory reference. Running this as a stand-alone
|
||||
// Roc program, generates the following error message:
|
||||
//
|
||||
// Application crashed with message
|
||||
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||
// Shutting down
|
||||
//
|
||||
// TODO: eventually we should insert the empty type for unresolved type
|
||||
// variables, since that means they're unbound.
|
||||
expect_runtime_error_panic!("List.any [] (\\_ -> True)");
|
||||
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2695,18 +2737,8 @@ fn list_all() {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)]
|
||||
fn list_all_empty_with_unknown_element_type() {
|
||||
// Segfaults with invalid memory reference. Running this as a stand-alone
|
||||
// Roc program, generates the following error message:
|
||||
//
|
||||
// Application crashed with message
|
||||
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||
// Shutting down
|
||||
//
|
||||
// TODO: eventually we should insert the empty type for unresolved type
|
||||
// variables, since that means they're unbound.
|
||||
expect_runtime_error_panic!("List.all [] (\\_ -> True)");
|
||||
assert_evals_to!("List.all [] (\\_ -> True)", true, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2975,9 +3007,6 @@ fn call_function_in_empty_list() {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// TODO to be improved, if we can generate the void function type for the function in the list,
|
||||
// this should succeed.
|
||||
#[should_panic(expected = r#"Roc failed with message: "#)]
|
||||
fn call_function_in_empty_list_unbound() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -2589,8 +2589,7 @@ fn module_thunk_is_function() {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[should_panic(expected = "Roc failed with message: ")]
|
||||
fn hit_unresolved_type_variable() {
|
||||
fn pass_through_unresolved_type_variable() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -182,3 +182,9 @@ impl<T: FromWasm32Memory, U: FromWasm32Memory, V: FromWasm32Memory> FromWasm32Me
|
||||
(t, u, v)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasm32Memory for std::convert::Infallible {
|
||||
fn decode(_memory_bytes: &[u8], _offset: u32) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user