diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d2b45afc9..02ec518c95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,14 +8,14 @@ env: jobs: prep-dependency-container: name: fmt, clippy, test --release - runs-on: self-hosted + runs-on: [self-hosted] timeout-minutes: 60 env: FORCE_COLOR: 1 steps: - uses: actions/checkout@v2 with: - clean: "false" + clean: "true" - name: Earthly version run: earthly --version diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index e7f40f2b7d..c0d8f9b39e 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -68,28 +68,53 @@ const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(5); pub fn types() -> MutMap { let mut types = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher()); - let mut add_type = |symbol, typ| { - debug_assert!( - !types.contains_key(&symbol), - "Duplicate type definition for {:?}", - symbol - ); + macro_rules! add_type { + ($symbol:expr, $typ:expr $(,)?) => {{ + debug_assert!( + !types.contains_key(&$symbol), + "Duplicate type definition for {:?}", + $symbol + ); - // TODO instead of using Region::zero for all of these, - // instead use the Region where they were defined in their - // source .roc files! This can give nicer error messages. - types.insert(symbol, (typ, Region::zero())); - }; + // TODO instead of using Region::zero for all of these, + // instead use the Region where they were defined in their + // source .roc files! This can give nicer error messages. + types.insert($symbol, ($typ, Region::zero())); + }}; + } + + macro_rules! add_top_level_function_type { + ($symbol:expr, $arguments:expr, $result:expr $(,)?) => {{ + debug_assert!( + !types.contains_key(&$symbol), + "Duplicate type definition for {:?}", + $symbol + ); + + let ext = Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)); + // in the future, we will enable the line below + // let closure_var = Box::new(SolvedType::TagUnion( + // vec![(TagName::Closure($symbol), vec![])], + // ext, + // )); + let closure_var = ext; + + let typ = SolvedType::Func($arguments, closure_var, $result); + + // TODO instead of using Region::zero for all of these, + // instead use the Region where they were defined in their + // source .roc files! This can give nicer error messages. + types.insert($symbol, (typ, Region::zero())); + }}; + } // Num module // add or (+) : Num a, Num a -> Num a - add_type( + add_top_level_function_type!( Symbol::NUM_ADD, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(num_type(flex(TVAR1))), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(num_type(flex(TVAR1))), ); fn overflow() -> SolvedType { @@ -100,190 +125,171 @@ pub fn types() -> MutMap { } // addChecked : Num a, Num a -> Result (Num a) [ Overflow ]* - add_type( + add_top_level_function_type!( Symbol::NUM_ADD_CHECKED, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(result_type(num_type(flex(TVAR1)), overflow())), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(result_type(num_type(flex(TVAR1)), overflow())), ); // addWrap : Int range, Int range -> Int range - add_type( + add_top_level_function_type!( Symbol::NUM_ADD_WRAP, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // sub or (-) : Num a, Num a -> Num a - add_type( + add_top_level_function_type!( Symbol::NUM_SUB, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(num_type(flex(TVAR1))), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(num_type(flex(TVAR1))), ); // subWrap : Int range, Int range -> Int range - add_type( + add_top_level_function_type!( Symbol::NUM_SUB_WRAP, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // subChecked : Num a, Num a -> Result (Num a) [ Overflow ]* - add_type( + add_top_level_function_type!( Symbol::NUM_SUB_CHECKED, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(result_type(num_type(flex(TVAR1)), overflow())), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(result_type(num_type(flex(TVAR1)), overflow())), ); // mul or (*) : Num a, Num a -> Num a - add_type( + add_top_level_function_type!( Symbol::NUM_MUL, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(num_type(flex(TVAR1))), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(num_type(flex(TVAR1))), ); // mulWrap : Int range, Int range -> Int range - add_type( + add_top_level_function_type!( Symbol::NUM_MUL_WRAP, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]* - add_type( + add_top_level_function_type!( Symbol::NUM_MUL_CHECKED, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(result_type(num_type(flex(TVAR1)), overflow())), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(result_type(num_type(flex(TVAR1)), overflow())), ); // abs : Num a -> Num a - add_type( + add_top_level_function_type!( Symbol::NUM_ABS, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))), + vec![num_type(flex(TVAR1))], + Box::new(num_type(flex(TVAR1))) ); // neg : Num a -> Num a - add_type( + add_top_level_function_type!( Symbol::NUM_NEG, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))), + vec![num_type(flex(TVAR1))], + Box::new(num_type(flex(TVAR1))) ); // isEq or (==) : a, a -> Bool - add_type( + add_top_level_function_type!( Symbol::BOOL_EQ, - top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())), + vec![flex(TVAR1), flex(TVAR1)], + Box::new(bool_type()) ); // isNeq or (!=) : a, a -> Bool - add_type( + add_top_level_function_type!( Symbol::BOOL_NEQ, - top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())), + vec![flex(TVAR1), flex(TVAR1)], + Box::new(bool_type()) ); // isLt or (<) : Num a, Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_LT, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(bool_type()), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(bool_type()), ); // isLte or (<=) : Num a, Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_LTE, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(bool_type()), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(bool_type()), ); // isGt or (>) : Num a, Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_GT, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(bool_type()), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(bool_type()), ); // isGte or (>=) : Num a, Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_GTE, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(bool_type()), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(bool_type()), ); // compare : Num a, Num a -> [ LT, EQ, GT ] - add_type( + add_top_level_function_type!( Symbol::NUM_COMPARE, - top_level_function( - vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(ordering_type()), - ), + vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], + Box::new(ordering_type()), ); // toFloat : Num * -> Float * - add_type( + add_top_level_function_type!( Symbol::NUM_TO_FLOAT, - top_level_function( - vec![num_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR2))), - ), + vec![num_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR2))), ); // isNegative : Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_NEGATIVE, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), + vec![num_type(flex(TVAR1))], + Box::new(bool_type()) ); // isPositive : Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_POSITIVE, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), + vec![num_type(flex(TVAR1))], + Box::new(bool_type()) ); // isZero : Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_ZERO, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), + vec![num_type(flex(TVAR1))], + Box::new(bool_type()) ); // isEven : Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_EVEN, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), + vec![num_type(flex(TVAR1))], + Box::new(bool_type()) ); // isOdd : Num a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_ODD, - top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())), + vec![num_type(flex(TVAR1))], + Box::new(bool_type()) ); // maxInt : Int range - add_type(Symbol::NUM_MAX_INT, int_type(flex(TVAR1))); + add_type!(Symbol::NUM_MAX_INT, int_type(flex(TVAR1))); // minInt : Int range - add_type(Symbol::NUM_MIN_INT, int_type(flex(TVAR1))); + add_type!(Symbol::NUM_MIN_INT, int_type(flex(TVAR1))); // div : Int, Int -> Result Int [ DivByZero ]* let div_by_zero = SolvedType::TagUnion( @@ -291,122 +297,99 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::NUM_DIV_INT, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ); // bitwiseAnd : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_BITWISE_AND, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // bitwiseXor : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_BITWISE_XOR, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // bitwiseOr : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_BITWISE_OR, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // shiftLeftBy : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_SHIFT_LEFT, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // shiftRightBy : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_SHIFT_RIGHT, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // shiftRightZfBy : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_SHIFT_RIGHT_ZERO_FILL, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // intCast : Int a -> Int b - add_type( + add_top_level_function_type!( Symbol::NUM_INT_CAST, - top_level_function(vec![int_type(flex(TVAR1))], Box::new(int_type(flex(TVAR2)))), + vec![int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))) ); // rem : Int a, Int a -> Result (Int a) [ DivByZero ]* - add_type( + add_top_level_function_type!( Symbol::NUM_REM, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ); // mod : Int a, Int a -> Result (Int a) [ DivByZero ]* - add_type( + add_top_level_function_type!( Symbol::NUM_MOD_INT, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), ); // isMultipleOf : Int a, Int a -> Bool - add_type( + add_top_level_function_type!( Symbol::NUM_IS_MULTIPLE_OF, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(bool_type()), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(bool_type()), ); // maxI128 : I128 - add_type(Symbol::NUM_MAX_I128, i128_type()); + add_type!(Symbol::NUM_MAX_I128, i128_type()); // Float module // div : Float a, Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_DIV_FLOAT, - top_level_function( - vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], - Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())), - ), + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())), ); // mod : Float a, Float a -> Result (Float a) [ DivByZero ]* - add_type( + add_top_level_function_type!( Symbol::NUM_MOD_FLOAT, - top_level_function( - vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], - Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)), - ), + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)), ); // sqrt : Float a -> Float a @@ -415,12 +398,10 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::NUM_SQRT, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)), - ), + vec![float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)), ); // log : Float a -> Float a @@ -429,205 +410,184 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::NUM_LOG, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)), - ), + vec![float_type(flex(TVAR1))], + Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)), ); // round : Float a -> Int b - add_type( + add_top_level_function_type!( Symbol::NUM_ROUND, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR2))), - ), + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), ); // sin : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_SIN, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // cos : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_COS, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // tan : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_TAN, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // maxFloat : Float a - add_type(Symbol::NUM_MAX_FLOAT, float_type(flex(TVAR1))); + add_type!(Symbol::NUM_MAX_FLOAT, float_type(flex(TVAR1))); // minFloat : Float a - add_type(Symbol::NUM_MIN_FLOAT, float_type(flex(TVAR1))); + add_type!(Symbol::NUM_MIN_FLOAT, float_type(flex(TVAR1))); // pow : Float a, Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_POW, - top_level_function( - vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // ceiling : Float a -> Int b - add_type( + add_top_level_function_type!( Symbol::NUM_CEILING, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR2))), - ), + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), ); // powInt : Int a, Int a -> Int a - add_type( + add_top_level_function_type!( Symbol::NUM_POW_INT, - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR1))), - ), + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR1))), ); // floor : Float a -> Int b - add_type( + add_top_level_function_type!( Symbol::NUM_FLOOR, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(int_type(flex(TVAR2))), - ), + vec![float_type(flex(TVAR1))], + Box::new(int_type(flex(TVAR2))), ); // atan : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_ATAN, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // acos : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_ACOS, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // asin : Float a -> Float a - add_type( + add_top_level_function_type!( Symbol::NUM_ASIN, - top_level_function( - vec![float_type(flex(TVAR1))], - Box::new(float_type(flex(TVAR1))), - ), + vec![float_type(flex(TVAR1))], + Box::new(float_type(flex(TVAR1))), ); // Bool module // and : Bool, Bool -> Bool - add_type( + add_top_level_function_type!( Symbol::BOOL_AND, - top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())), + vec![bool_type(), bool_type()], + Box::new(bool_type()) ); // or : Bool, Bool -> Bool - add_type( + add_top_level_function_type!( Symbol::BOOL_OR, - top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())), + vec![bool_type(), bool_type()], + Box::new(bool_type()) ); // xor : Bool, Bool -> Bool - add_type( + add_top_level_function_type!( Symbol::BOOL_XOR, - top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())), + vec![bool_type(), bool_type()], + Box::new(bool_type()) ); // not : Bool -> Bool - add_type( - Symbol::BOOL_NOT, - top_level_function(vec![bool_type()], Box::new(bool_type())), - ); + add_top_level_function_type!(Symbol::BOOL_NOT, vec![bool_type()], Box::new(bool_type())); // Str module // Str.split : Str, Str -> List Str - add_type( + add_top_level_function_type!( Symbol::STR_SPLIT, - top_level_function( - vec![str_type(), str_type()], - Box::new(list_type(str_type())), - ), + vec![str_type(), str_type()], + Box::new(list_type(str_type())), ); // Str.concat : Str, Str -> Str - add_type( + add_top_level_function_type!( Symbol::STR_CONCAT, - top_level_function(vec![str_type(), str_type()], Box::new(str_type())), + vec![str_type(), str_type()], + Box::new(str_type()), ); // Str.joinWith : List Str, Str -> Str - add_type( + add_top_level_function_type!( Symbol::STR_JOIN_WITH, - top_level_function( - vec![list_type(str_type()), str_type()], - Box::new(str_type()), - ), + vec![list_type(str_type()), str_type()], + Box::new(str_type()), ); // isEmpty : Str -> Bool - add_type( + add_top_level_function_type!( Symbol::STR_IS_EMPTY, - top_level_function(vec![str_type()], Box::new(bool_type())), + vec![str_type()], + Box::new(bool_type()) ); // startsWith : Str, Str -> Bool - add_type( + add_top_level_function_type!( Symbol::STR_STARTS_WITH, - top_level_function(vec![str_type(), str_type()], Box::new(bool_type())), + vec![str_type(), str_type()], + Box::new(bool_type()) ); // startsWithCodePoint : Str, U32 -> Bool - add_type( + add_top_level_function_type!( Symbol::STR_STARTS_WITH_CODE_POINT, - top_level_function(vec![str_type(), u32_type()], Box::new(bool_type())), + vec![str_type(), u32_type()], + Box::new(bool_type()) ); // endsWith : Str, Str -> Bool - add_type( + add_top_level_function_type!( Symbol::STR_ENDS_WITH, - top_level_function(vec![str_type(), str_type()], Box::new(bool_type())), + vec![str_type(), str_type()], + Box::new(bool_type()) ); // countGraphemes : Str -> Nat - add_type( + add_top_level_function_type!( Symbol::STR_COUNT_GRAPHEMES, - top_level_function(vec![str_type()], Box::new(nat_type())), + vec![str_type()], + Box::new(nat_type()) ); // fromInt : Int a -> Str - add_type( + add_top_level_function_type!( Symbol::STR_FROM_INT, - top_level_function(vec![int_type(flex(TVAR1))], Box::new(str_type())), + vec![int_type(flex(TVAR1))], + Box::new(str_type()) ); // fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]* @@ -640,24 +600,24 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::STR_FROM_UTF8, - top_level_function( - vec![list_type(u8_type())], - Box::new(result_type(str_type(), bad_utf8)), - ), + vec![list_type(u8_type())], + Box::new(result_type(str_type(), bad_utf8)), ); // toBytes : Str -> List U8 - add_type( + add_top_level_function_type!( Symbol::STR_TO_BYTES, - top_level_function(vec![str_type()], Box::new(list_type(u8_type()))), + vec![str_type()], + Box::new(list_type(u8_type())) ); // fromFloat : Float a -> Str - add_type( + add_top_level_function_type!( Symbol::STR_FROM_FLOAT, - top_level_function(vec![float_type(flex(TVAR1))], Box::new(str_type())), + vec![float_type(flex(TVAR1))], + Box::new(str_type()) ); // List module @@ -668,12 +628,10 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::LIST_GET, - top_level_function( - vec![list_type(flex(TVAR1)), nat_type()], - Box::new(result_type(flex(TVAR1), index_out_of_bounds)), - ), + vec![list_type(flex(TVAR1)), nat_type()], + Box::new(result_type(flex(TVAR1), index_out_of_bounds)), ); // first : List elem -> Result elem [ ListWasEmpty ]* @@ -682,92 +640,74 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::LIST_FIRST, - top_level_function( - vec![list_type(flex(TVAR1))], - Box::new(result_type(flex(TVAR1), list_was_empty.clone())), - ), + vec![list_type(flex(TVAR1))], + Box::new(result_type(flex(TVAR1), list_was_empty.clone())), ); // last : List elem -> Result elem [ ListWasEmpty ]* - add_type( + add_top_level_function_type!( Symbol::LIST_LAST, - top_level_function( - vec![list_type(flex(TVAR1))], - Box::new(result_type(flex(TVAR1), list_was_empty)), - ), + vec![list_type(flex(TVAR1))], + Box::new(result_type(flex(TVAR1), list_was_empty)), ); // set : List elem, Nat, elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_SET, - top_level_function( - vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)], + Box::new(list_type(flex(TVAR1))), ); // concat : List elem, List elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_CONCAT, - top_level_function( - vec![list_type(flex(TVAR1)), list_type(flex(TVAR1))], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1)), list_type(flex(TVAR1))], + Box::new(list_type(flex(TVAR1))), ); // contains : List elem, elem -> Bool - add_type( + add_top_level_function_type!( Symbol::LIST_CONTAINS, - top_level_function( - vec![list_type(flex(TVAR1)), flex(TVAR1)], - Box::new(bool_type()), - ), + vec![list_type(flex(TVAR1)), flex(TVAR1)], + Box::new(bool_type()), ); // sum : List (Num a) -> Num a - add_type( + add_top_level_function_type!( Symbol::LIST_SUM, - top_level_function( - vec![list_type(num_type(flex(TVAR1)))], - Box::new(num_type(flex(TVAR1))), - ), + vec![list_type(num_type(flex(TVAR1)))], + Box::new(num_type(flex(TVAR1))), ); // product : List (Num a) -> Num a - add_type( + add_top_level_function_type!( Symbol::LIST_PRODUCT, - top_level_function( - vec![list_type(num_type(flex(TVAR1)))], - Box::new(num_type(flex(TVAR1))), - ), + vec![list_type(num_type(flex(TVAR1)))], + Box::new(num_type(flex(TVAR1))), ); // walk : List elem, (elem -> accum -> accum), accum -> accum - add_type( + add_top_level_function_type!( Symbol::LIST_WALK, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), - flex(TVAR2), - ], - Box::new(flex(TVAR2)), - ), + vec![ + list_type(flex(TVAR1)), + closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), + flex(TVAR2), + ], + Box::new(flex(TVAR2)), ); // walkBackwards : List elem, (elem -> accum -> accum), accum -> accum - add_type( + add_top_level_function_type!( Symbol::LIST_WALK_BACKWARDS, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), - flex(TVAR2), - ], - Box::new(flex(TVAR2)), - ), + vec![ + list_type(flex(TVAR1)), + closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), + flex(TVAR2), + ], + Box::new(flex(TVAR2)), ); fn until_type(content: SolvedType) -> SolvedType { @@ -782,38 +722,35 @@ pub fn types() -> MutMap { } // walkUntil : List elem, (elem -> accum -> [ Continue accum, Stop accum ]), accum -> accum - add_type( + add_top_level_function_type!( Symbol::LIST_WALK_UNTIL, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure( - vec![flex(TVAR1), flex(TVAR2)], - TVAR3, - Box::new(until_type(flex(TVAR2))), - ), - flex(TVAR2), - ], - Box::new(flex(TVAR2)), - ), + vec![ + list_type(flex(TVAR1)), + closure( + vec![flex(TVAR1), flex(TVAR2)], + TVAR3, + Box::new(until_type(flex(TVAR2))), + ), + flex(TVAR2), + ], + Box::new(flex(TVAR2)), ); // keepIf : List elem, (elem -> Bool) -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_KEEP_IF, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())), - ], - Box::new(list_type(flex(TVAR1))), - ), + vec![ + list_type(flex(TVAR1)), + closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())), + ], + Box::new(list_type(flex(TVAR1))), ); // keepOks : List before, (before -> Result after *) -> List after - add_type(Symbol::LIST_KEEP_OKS, { + { let_tvars! { star, cvar, before, after}; - top_level_function( + add_top_level_function_type!( + Symbol::LIST_KEEP_OKS, vec![ list_type(flex(before)), closure( @@ -824,12 +761,14 @@ pub fn types() -> MutMap { ], Box::new(list_type(flex(after))), ) - }); + }; // keepErrs: List before, (before -> Result * after) -> List after - add_type(Symbol::LIST_KEEP_ERRS, { + { let_tvars! { star, cvar, before, after}; - top_level_function( + + add_top_level_function_type!( + Symbol::LIST_KEEP_ERRS, vec![ list_type(flex(before)), closure( @@ -840,44 +779,43 @@ pub fn types() -> MutMap { ], Box::new(list_type(flex(after))), ) - }); + }; // range : Int a, Int a -> List (Int a) - add_type(Symbol::LIST_RANGE, { - top_level_function( - vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], - Box::new(list_type(int_type(flex(TVAR1)))), - ) - }); + add_top_level_function_type!( + Symbol::LIST_RANGE, + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(list_type(int_type(flex(TVAR1)))), + ); // map : List before, (before -> after) -> List after - add_type( + add_top_level_function_type!( Symbol::LIST_MAP, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure(vec![flex(TVAR1)], TVAR3, Box::new(flex(TVAR2))), - ], - Box::new(list_type(flex(TVAR2))), - ), + vec![ + list_type(flex(TVAR1)), + closure(vec![flex(TVAR1)], TVAR3, Box::new(flex(TVAR2))), + ], + Box::new(list_type(flex(TVAR2))), ); // mapWithIndex : List before, (Nat, before -> after) -> List after - add_type(Symbol::LIST_MAP_WITH_INDEX, { + { let_tvars! { cvar, before, after}; - top_level_function( + add_top_level_function_type!( + Symbol::LIST_MAP_WITH_INDEX, vec![ list_type(flex(before)), closure(vec![nat_type(), flex(before)], cvar, Box::new(flex(after))), ], Box::new(list_type(flex(after))), ) - }); + }; // map2 : List a, List b, (a, b -> c) -> List c - add_type(Symbol::LIST_MAP2, { + { let_tvars! {a, b, c, cvar}; - top_level_function( + add_top_level_function_type!( + Symbol::LIST_MAP2, vec![ list_type(flex(a)), list_type(flex(b)), @@ -885,13 +823,14 @@ pub fn types() -> MutMap { ], Box::new(list_type(flex(c))), ) - }); + }; - // map3 : List a, List b, List c, (a, b, c -> d) -> List d - add_type(Symbol::LIST_MAP3, { + { let_tvars! {a, b, c, d, cvar}; - top_level_function( + // map3 : List a, List b, List c, (a, b, c -> d) -> List d + add_top_level_function_type!( + Symbol::LIST_MAP3, vec![ list_type(flex(a)), list_type(flex(b)), @@ -900,114 +839,102 @@ pub fn types() -> MutMap { ], Box::new(list_type(flex(d))), ) - }); + }; // append : List elem, elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_APPEND, - top_level_function( - vec![list_type(flex(TVAR1)), flex(TVAR1)], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1)), flex(TVAR1)], + Box::new(list_type(flex(TVAR1))), ); // prepend : List elem, elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_PREPEND, - top_level_function( - vec![list_type(flex(TVAR1)), flex(TVAR1)], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1)), flex(TVAR1)], + Box::new(list_type(flex(TVAR1))), ); // join : List (List elem) -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_JOIN, - top_level_function( - vec![list_type(list_type(flex(TVAR1)))], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(list_type(flex(TVAR1)))], + Box::new(list_type(flex(TVAR1))), ); // single : a -> List a - add_type( + add_top_level_function_type!( Symbol::LIST_SINGLE, - top_level_function(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))), + vec![flex(TVAR1)], + Box::new(list_type(flex(TVAR1))) ); // repeat : Nat, elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_REPEAT, - top_level_function( - vec![nat_type(), flex(TVAR1)], - Box::new(list_type(flex(TVAR1))), - ), + vec![nat_type(), flex(TVAR1)], + Box::new(list_type(flex(TVAR1))), ); // reverse : List elem -> List elem - add_type( + add_top_level_function_type!( Symbol::LIST_REVERSE, - top_level_function( - vec![list_type(flex(TVAR1))], - Box::new(list_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1))], + Box::new(list_type(flex(TVAR1))), ); // len : List * -> Nat - add_type( + add_top_level_function_type!( Symbol::LIST_LEN, - top_level_function(vec![list_type(flex(TVAR1))], Box::new(nat_type())), + vec![list_type(flex(TVAR1))], + Box::new(nat_type()) ); // isEmpty : List * -> Bool - add_type( + add_top_level_function_type!( Symbol::LIST_IS_EMPTY, - top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())), + vec![list_type(flex(TVAR1))], + Box::new(bool_type()) ); // sortWith : List a, (a, a -> Ordering) -> List a - add_type( + add_top_level_function_type!( Symbol::LIST_SORT_WITH, - top_level_function( - vec![ - list_type(flex(TVAR1)), - closure( - vec![flex(TVAR1), flex(TVAR1)], - TVAR2, - Box::new(ordering_type()), - ), - ], - Box::new(list_type(flex(TVAR1))), - ), + vec![ + list_type(flex(TVAR1)), + closure( + vec![flex(TVAR1), flex(TVAR1)], + TVAR2, + Box::new(ordering_type()), + ), + ], + Box::new(list_type(flex(TVAR1))), ); // Dict module // Dict.hashTestOnly : Nat, v -> Nat - add_type( + add_top_level_function_type!( Symbol::DICT_TEST_HASH, - top_level_function(vec![u64_type(), flex(TVAR2)], Box::new(nat_type())), + vec![u64_type(), flex(TVAR2)], + Box::new(nat_type()) ); // len : Dict * * -> Nat - add_type( + add_top_level_function_type!( Symbol::DICT_LEN, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2))], - Box::new(nat_type()), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2))], + Box::new(nat_type()), ); // empty : Dict * * - add_type(Symbol::DICT_EMPTY, dict_type(flex(TVAR1), flex(TVAR2))); + add_type!(Symbol::DICT_EMPTY, dict_type(flex(TVAR1), flex(TVAR2))); // single : k, v -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_SINGLE, - top_level_function( - vec![flex(TVAR1), flex(TVAR2)], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![flex(TVAR1), flex(TVAR2)], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // get : Dict k v, k -> Result v [ KeyNotFound ]* @@ -1016,264 +943,220 @@ pub fn types() -> MutMap { Box::new(SolvedType::Wildcard), ); - add_type( + add_top_level_function_type!( Symbol::DICT_GET, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], - Box::new(result_type(flex(TVAR2), key_not_found)), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], + Box::new(result_type(flex(TVAR2), key_not_found)), ); // Dict.insert : Dict k v, k, v -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_INSERT, - top_level_function( - vec![ - dict_type(flex(TVAR1), flex(TVAR2)), - flex(TVAR1), - flex(TVAR2), - ], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![ + dict_type(flex(TVAR1), flex(TVAR2)), + flex(TVAR1), + flex(TVAR2), + ], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // Dict.remove : Dict k v, k -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_REMOVE, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // Dict.contains : Dict k v, k -> Bool - add_type( + add_top_level_function_type!( Symbol::DICT_CONTAINS, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], - Box::new(bool_type()), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)], + Box::new(bool_type()), ); // Dict.keys : Dict k v -> List k - add_type( + add_top_level_function_type!( Symbol::DICT_KEYS, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2))], - Box::new(list_type(flex(TVAR1))), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2))], + Box::new(list_type(flex(TVAR1))), ); // Dict.values : Dict k v -> List v - add_type( + add_top_level_function_type!( Symbol::DICT_VALUES, - top_level_function( - vec![dict_type(flex(TVAR1), flex(TVAR2))], - Box::new(list_type(flex(TVAR2))), - ), + vec![dict_type(flex(TVAR1), flex(TVAR2))], + Box::new(list_type(flex(TVAR2))), ); // Dict.union : Dict k v, Dict k v -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_UNION, - top_level_function( - vec![ - dict_type(flex(TVAR1), flex(TVAR2)), - dict_type(flex(TVAR1), flex(TVAR2)), - ], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![ + dict_type(flex(TVAR1), flex(TVAR2)), + dict_type(flex(TVAR1), flex(TVAR2)), + ], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // Dict.intersection : Dict k v, Dict k v -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_INTERSECTION, - top_level_function( - vec![ - dict_type(flex(TVAR1), flex(TVAR2)), - dict_type(flex(TVAR1), flex(TVAR2)), - ], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![ + dict_type(flex(TVAR1), flex(TVAR2)), + dict_type(flex(TVAR1), flex(TVAR2)), + ], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // Dict.difference : Dict k v, Dict k v -> Dict k v - add_type( + add_top_level_function_type!( Symbol::DICT_DIFFERENCE, - top_level_function( - vec![ - dict_type(flex(TVAR1), flex(TVAR2)), - dict_type(flex(TVAR1), flex(TVAR2)), - ], - Box::new(dict_type(flex(TVAR1), flex(TVAR2))), - ), + vec![ + dict_type(flex(TVAR1), flex(TVAR2)), + dict_type(flex(TVAR1), flex(TVAR2)), + ], + Box::new(dict_type(flex(TVAR1), flex(TVAR2))), ); // Dict.walk : Dict k v, (k, v, accum -> accum), accum -> accum - add_type( + add_top_level_function_type!( Symbol::DICT_WALK, - top_level_function( - vec![ - dict_type(flex(TVAR1), flex(TVAR2)), - closure( - vec![flex(TVAR1), flex(TVAR2), flex(TVAR3)], - TVAR4, - Box::new(flex(TVAR3)), - ), - flex(TVAR3), - ], - Box::new(flex(TVAR3)), - ), + vec![ + dict_type(flex(TVAR1), flex(TVAR2)), + closure( + vec![flex(TVAR1), flex(TVAR2), flex(TVAR3)], + TVAR4, + Box::new(flex(TVAR3)), + ), + flex(TVAR3), + ], + Box::new(flex(TVAR3)), ); // Set module // empty : Set a - add_type(Symbol::SET_EMPTY, set_type(flex(TVAR1))); + add_type!(Symbol::SET_EMPTY, set_type(flex(TVAR1))); // single : a -> Set a - add_type( + add_top_level_function_type!( Symbol::SET_SINGLE, - top_level_function(vec![flex(TVAR1)], Box::new(set_type(flex(TVAR1)))), + vec![flex(TVAR1)], + Box::new(set_type(flex(TVAR1))) ); // len : Set * -> Nat - add_type( + add_top_level_function_type!( Symbol::SET_LEN, - top_level_function(vec![set_type(flex(TVAR1))], Box::new(nat_type())), + vec![set_type(flex(TVAR1))], + Box::new(nat_type()) ); // toList : Set a -> List a - add_type( + add_top_level_function_type!( Symbol::SET_TO_LIST, - top_level_function( - vec![set_type(flex(TVAR1))], - Box::new(list_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1))], + Box::new(list_type(flex(TVAR1))), ); - // fromList : Set a -> List a - add_type( + // fromList : List a -> Set a + add_top_level_function_type!( Symbol::SET_FROM_LIST, - top_level_function( - vec![list_type(flex(TVAR1))], - Box::new(set_type(flex(TVAR1))), - ), + vec![list_type(flex(TVAR1))], + Box::new(set_type(flex(TVAR1))), ); // union : Set a, Set a -> Set a - add_type( + add_top_level_function_type!( Symbol::SET_UNION, - top_level_function( - vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], - Box::new(set_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], + Box::new(set_type(flex(TVAR1))), ); // difference : Set a, Set a -> Set a - add_type( + add_top_level_function_type!( Symbol::SET_DIFFERENCE, - top_level_function( - vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], - Box::new(set_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], + Box::new(set_type(flex(TVAR1))), ); // intersection : Set a, Set a -> Set a - add_type( + add_top_level_function_type!( Symbol::SET_INTERSECTION, - top_level_function( - vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], - Box::new(set_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))], + Box::new(set_type(flex(TVAR1))), ); // Set.walk : Set a, (a, b -> b), b -> b - add_type( + add_top_level_function_type!( Symbol::SET_WALK, - top_level_function( - vec![ - set_type(flex(TVAR1)), - closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), - flex(TVAR2), - ], - Box::new(flex(TVAR2)), - ), + vec![ + set_type(flex(TVAR1)), + closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))), + flex(TVAR2), + ], + Box::new(flex(TVAR2)), ); - add_type( + add_top_level_function_type!( Symbol::SET_INSERT, - top_level_function( - vec![set_type(flex(TVAR1)), flex(TVAR1)], - Box::new(set_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1)), flex(TVAR1)], + Box::new(set_type(flex(TVAR1))), ); - add_type( + add_top_level_function_type!( Symbol::SET_REMOVE, - top_level_function( - vec![set_type(flex(TVAR1)), flex(TVAR1)], - Box::new(set_type(flex(TVAR1))), - ), + vec![set_type(flex(TVAR1)), flex(TVAR1)], + Box::new(set_type(flex(TVAR1))), ); - add_type( + add_top_level_function_type!( Symbol::SET_CONTAINS, - top_level_function( - vec![set_type(flex(TVAR1)), flex(TVAR1)], - Box::new(bool_type()), - ), + vec![set_type(flex(TVAR1)), flex(TVAR1)], + Box::new(bool_type()), ); // Result module // map : Result a err, (a -> b) -> Result b err - add_type( + add_top_level_function_type!( Symbol::RESULT_MAP, - top_level_function( - vec![ - result_type(flex(TVAR1), flex(TVAR3)), - closure(vec![flex(TVAR1)], TVAR4, Box::new(flex(TVAR2))), - ], - Box::new(result_type(flex(TVAR2), flex(TVAR3))), - ), + vec![ + result_type(flex(TVAR1), flex(TVAR3)), + closure(vec![flex(TVAR1)], TVAR4, Box::new(flex(TVAR2))), + ], + Box::new(result_type(flex(TVAR2), flex(TVAR3))), ); // mapErr : Result a x, (x -> y) -> Result a x - add_type( + add_top_level_function_type!( Symbol::RESULT_MAP_ERR, - top_level_function( - vec![ - result_type(flex(TVAR1), flex(TVAR3)), - closure(vec![flex(TVAR3)], TVAR4, Box::new(flex(TVAR2))), - ], - Box::new(result_type(flex(TVAR1), flex(TVAR2))), - ), + vec![ + result_type(flex(TVAR1), flex(TVAR3)), + closure(vec![flex(TVAR3)], TVAR4, Box::new(flex(TVAR2))), + ], + Box::new(result_type(flex(TVAR1), flex(TVAR2))), ); // after : Result a err, (a -> Result b err) -> Result b err - add_type( + add_top_level_function_type!( Symbol::RESULT_AFTER, - top_level_function( - vec![ - result_type(flex(TVAR1), flex(TVAR3)), - closure( - vec![flex(TVAR1)], - TVAR4, - Box::new(result_type(flex(TVAR2), flex(TVAR3))), - ), - ], - Box::new(result_type(flex(TVAR2), flex(TVAR3))), - ), + vec![ + result_type(flex(TVAR1), flex(TVAR3)), + closure( + vec![flex(TVAR1)], + TVAR4, + Box::new(result_type(flex(TVAR2), flex(TVAR3))), + ), + ], + Box::new(result_type(flex(TVAR2), flex(TVAR3))), ); // withDefault : Result a x, a -> a - add_type( + add_top_level_function_type!( Symbol::RESULT_WITH_DEFAULT, - top_level_function( - vec![result_type(flex(TVAR1), flex(TVAR3)), flex(TVAR1)], - Box::new(flex(TVAR1)), - ), + vec![result_type(flex(TVAR1), flex(TVAR3)), flex(TVAR1)], + Box::new(flex(TVAR1)), ); types @@ -1284,15 +1167,6 @@ fn flex(tvar: VarId) -> SolvedType { SolvedType::Flex(tvar) } -#[inline(always)] -fn top_level_function(arguments: Vec, ret: Box) -> SolvedType { - SolvedType::Func( - arguments, - Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)), - ret, - ) -} - #[inline(always)] fn closure(arguments: Vec, closure_var: VarId, ret: Box) -> SolvedType { SolvedType::Func(arguments, Box::new(SolvedType::Flex(closure_var)), ret) diff --git a/editor/editor-ideas.md b/editor/editor-ideas.md index 92a7a796e9..41ff4dbfff 100644 --- a/editor/editor-ideas.md +++ b/editor/editor-ideas.md @@ -37,10 +37,11 @@ Nice collection of research on innovative editors, [link](https://futureofcoding * [VS code debug visualization](https://marketplace.visualstudio.com/items?itemName=hediet.debug-visualizer) * [Algorithm visualization for javascript](https://algorithm-visualizer.org) * [godbolt.org Compiler Explorer](https://godbolt.org/) -* Say you have a failing test that used to work, it would be very valuable to see all code that was changed that was used only by that test. -e.g. you have a test `calculate_sum_test` that only uses the function `add`, when the test fails you should be able to see a diff showing only what changed for the function `add`. It would also be great to have a diff of [expression values](https://homepages.cwi.nl/~storm/livelit/images/bret.png) Bret Victor style. An ambitious project would be to suggest or automatically try fixes based on these diffs. * [whitebox debug visualization](https://vimeo.com/483795097) * [Hest](https://ivanish.ca/hest-time-travel/) tool for making highly interactive simulations. +* Say you have a failing test that used to work, it would be very valuable to see all code that was changed that was used only by that test. +e.g. you have a test `calculate_sum_test` that only uses the function `add`, when the test fails you should be able to see a diff showing only what changed for the function `add`. It would also be great to have a diff of [expression values](https://homepages.cwi.nl/~storm/livelit/images/bret.png) Bret Victor style. An ambitious project would be to suggest or automatically try fixes based on these diffs. +* I think it could be possible to create a minimal reproduction of a program / block of code / code used by a single test. So for a failing unit test I would expect it to extract imports, the platform, types and functions that are necessary to run only that unit test and put them in a standalone roc project. This would be useful for sharing bugs with library+application authors and colleagues, for profiling or debugging with all "clutter" removed. ### Structured Editing @@ -86,6 +87,12 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe * Mozilla DeepSpeech model runs fast, works pretty well for actions but would need additional training for code input. Possible to reuse [Mozilla common voice](https://github.com/common-voice/common-voice) for creating more "spoken code" data. +### Beginner-focused Features + + * Show Roc cheat sheet on start-up. + * Plugin that translates short pieces of code from another programming language to Roc. [Relevant research](https://www.youtube.com/watch?v=xTzFJIknh7E). Someone who only knows the R language could get started with Roc with less friction if they could quickly define a list R style (`lst <- c(1,2,3)`) and get it translated to Roc. + * Being able to asses or ask the user for the amount of experience they have with Roc would be a valuable feature for recommending plugins, editor tips, recommending tutorials, automated error search (e.g searching common beginner errors first), ... . + ### Productivity features * When refactoring; @@ -106,6 +113,13 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe * Regex-like find and substitution based on plain english description and example (replacement). i.e. replace all `[` between double quotes with `{`. [Inspiration](https://alexmoltzau.medium.com/english-to-regex-thanks-to-gpt-3-13f03b68236e). * Show productivity tips based on behavior. i.e. if the user is scrolling through the error bar and clicking on the next error several times, show a tip with "go to next error" shortcut. * Command to "benchmark this function" or "benchmark this test" with flamegraph and execution time per line. +* Instead of going to definition and having to navigate back and forth between files, show an editable view inside the current file. See [this video](https://www.youtube.com/watch?v=EenznqbW5w8) +* When encountering an unexpected error in the user's program we show a button at the bottom to start an automated search on this error. The search would: + * look for similar errors in github issues of the relevant libraries + * search stackoverflow questions + * search a local history of previously encountered errors and fixes + * search through a database of our zullip questions + * ... #### Autocomplete @@ -124,6 +138,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe * [Codota](https://www.codota.com) AI autocomplete and example searching. * [Aroma](https://ai.facebook.com/blog/aroma-ml-for-code-recommendation) showing examples similar to current code. * [MISM](https://arxiv.org/abs/2006.05265) neural network based code similarity scoring. +* [Inquisitive code editor](https://web.eecs.utk.edu/~azh/blog/inquisitivecodeeditor.html) Interactive bug detection with doc+test generation. +* [NextJournal](https://nextjournal.com/joe-loco/command-bar?token=DpU6ewNQnLhYtVkwhs9GeX) Discoverable commands and shortcuts. ### Non-Code Related Inspiration diff --git a/editor/src/lang/constrain.rs b/editor/src/lang/constrain.rs index f9ae771a86..d2119581c9 100644 --- a/editor/src/lang/constrain.rs +++ b/editor/src/lang/constrain.rs @@ -9,7 +9,7 @@ use crate::lang::{ use roc_can::expected::Expected; use roc_collections::all::{BumpMap, BumpMapDefault, Index}; -use roc_module::symbol::Symbol; +use roc_module::{ident::TagName, symbol::Symbol}; use roc_region::all::{Located, Region}; use roc_types::{ subs::Variable, @@ -21,7 +21,7 @@ use roc_types::{ pub enum Constraint<'a> { Eq(Type2, Expected, Category, Region), // Store(Type, Variable, &'static str, u32), - // Lookup(Symbol, Expected, Region), + Lookup(Symbol, Expected, Region), // Pattern(Region, PatternCategory, Type, PExpected), And(BumpVec<'a, Constraint<'a>>), Let(&'a LetConstraint<'a>), @@ -52,6 +52,7 @@ pub fn constrain_expr<'a>( Expr2::SmallStr(_) => Eq(str_type(env.pool), expected, Category::Str, region), Expr2::Blank => True, Expr2::EmptyRecord => constrain_empty_record(expected, region), + Expr2::Var(symbol) => Lookup(*symbol, expected, region), Expr2::SmallInt { var, .. } => { let mut flex_vars = BumpVec::with_capacity_in(1, arena); @@ -216,6 +217,65 @@ pub fn constrain_expr<'a>( exists(arena, field_vars, And(constraints)) } } + Expr2::GlobalTag { + variant_var, + ext_var, + name, + arguments, + } => { + let mut flex_vars = BumpVec::with_capacity_in(arguments.len(), arena); + let types = PoolVec::with_capacity(arguments.len() as u32, env.pool); + let mut arg_cons = BumpVec::with_capacity_in(arguments.len(), arena); + + for (argument_node_id, type_node_id) in + arguments.iter_node_ids().zip(types.iter_node_ids()) + { + let (var, expr_node_id) = env.pool.get(argument_node_id); + + let argument_expr = env.pool.get(*expr_node_id); + + let arg_con = constrain_expr( + arena, + env, + argument_expr, + Expected::NoExpectation(Type2::Variable(*var)), + region, + ); + + arg_cons.push(arg_con); + flex_vars.push(*var); + + env.pool[type_node_id] = Type2::Variable(*var); + } + + let union_con = Eq( + Type2::TagUnion( + PoolVec::new(std::iter::once((*name, types)), env.pool), + env.pool.add(Type2::Variable(*ext_var)), + ), + expected.shallow_clone(), + Category::TagApply { + tag_name: TagName::Global(name.as_str(env.pool).into()), + args_count: arguments.len(), + }, + region, + ); + + let ast_con = Eq( + Type2::Variable(*variant_var), + expected, + Category::Storage(std::file!(), std::line!()), + region, + ); + + flex_vars.push(*variant_var); + flex_vars.push(*ext_var); + + arg_cons.push(union_con); + arg_cons.push(ast_con); + + exists(arena, flex_vars, And(arg_cons)) + } _ => todo!("implement constaints for {:?}", expr), } } @@ -268,13 +328,7 @@ fn empty_list_type(pool: &mut Pool, var: Variable) -> Type2 { #[inline(always)] fn list_type(pool: &mut Pool, typ: Type2) -> Type2 { - let args = PoolVec::with_capacity(1, pool); - - for (arg_node_id, arg) in args.iter_node_ids().zip(vec![typ]) { - pool[arg_node_id] = arg; - } - - builtin_type(Symbol::LIST_LIST, args) + builtin_type(Symbol::LIST_LIST, PoolVec::new(vec![typ].into_iter(), pool)) } #[inline(always)] diff --git a/editor/src/lang/solve.rs b/editor/src/lang/solve.rs index 3d7118e4f4..fa5e0473a7 100644 --- a/editor/src/lang/solve.rs +++ b/editor/src/lang/solve.rs @@ -1,7 +1,7 @@ #![allow(clippy::all)] #![allow(dead_code)] use crate::lang::constrain::Constraint::{self, *}; -use crate::lang::pool::Pool; +use crate::lang::pool::{Pool, ShallowClone}; use crate::lang::types::Type2; use bumpalo::Bump; use roc_can::expected::{Expected, PExpected}; @@ -270,75 +270,79 @@ fn solve<'a>( // } // } // } - // Lookup(symbol, expectation, region) => { - // match env.vars_by_symbol.get(&symbol) { - // Some(var) => { - // // Deep copy the vars associated with this symbol before unifying them. - // // Otherwise, suppose we have this: - // // - // // identity = \a -> a - // // - // // x = identity 5 - // // - // // When we call (identity 5), it's important that we not unify - // // on identity's original vars. If we do, the type of `identity` will be - // // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect; - // // the type of `identity` is more general than that! - // // - // // Instead, we want to unify on a *copy* of its vars. If the copy unifies - // // successfully (in this case, to `Int -> Int`), we can use that to - // // infer the type of this lookup (in this case, `Int`) without ever - // // having mutated the original. - // // - // // If this Lookup is targeting a value in another module, - // // then we copy from that module's Subs into our own. If the value - // // is being looked up in this module, then we use our Subs as both - // // the source and destination. - // let actual = deep_copy_var(subs, rank, pools, *var); - // let expected = type_to_var( - // subs, - // rank, - // pools, - // cached_aliases, - // expectation.get_type_ref(), - // ); - // match unify(subs, actual, expected) { - // Success(vars) => { - // introduce(subs, rank, pools, &vars); - // - // state - // } - // - // Failure(vars, actual_type, expected_type) => { - // introduce(subs, rank, pools, &vars); - // - // let problem = TypeError::BadExpr( - // *region, - // Category::Lookup(*symbol), - // actual_type, - // expectation.clone().replace(expected_type), - // ); - // - // problems.push(problem); - // - // state - // } - // BadType(vars, problem) => { - // introduce(subs, rank, pools, &vars); - // - // problems.push(TypeError::BadType(problem)); - // - // state - // } - // } - // } - // None => { - // problems.push(TypeError::UnexposedLookup(*symbol)); - // - // state - // } - // } - // } + Lookup(symbol, expectation, region) => { + match env.vars_by_symbol.get(&symbol) { + Some(var) => { + // Deep copy the vars associated with this symbol before unifying them. + // Otherwise, suppose we have this: + // + // identity = \a -> a + // + // x = identity 5 + // + // When we call (identity 5), it's important that we not unify + // on identity's original vars. If we do, the type of `identity` will be + // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect; + // the type of `identity` is more general than that! + // + // Instead, we want to unify on a *copy* of its vars. If the copy unifies + // successfully (in this case, to `Int -> Int`), we can use that to + // infer the type of this lookup (in this case, `Int`) without ever + // having mutated the original. + // + // If this Lookup is targeting a value in another module, + // then we copy from that module's Subs into our own. If the value + // is being looked up in this module, then we use our Subs as both + // the source and destination. + let actual = deep_copy_var(subs, rank, pools, *var); + + let expected = type_to_var( + arena, + mempool, + subs, + rank, + pools, + cached_aliases, + expectation.get_type_ref(), + ); + + match unify(subs, actual, expected) { + Success(vars) => { + introduce(subs, rank, pools, &vars); + + state + } + + Failure(vars, actual_type, expected_type) => { + introduce(subs, rank, pools, &vars); + + let problem = TypeError::BadExpr( + *region, + Category::Lookup(*symbol), + actual_type, + expectation.shallow_clone().replace(expected_type), + ); + + problems.push(problem); + + state + } + BadType(vars, problem) => { + introduce(subs, rank, pools, &vars); + + problems.push(TypeError::BadType(problem)); + + state + } + } + } + None => { + problems.push(TypeError::UnexposedLookup(*symbol)); + + state + } + } + } And(sub_constraints) => { let mut state = state; @@ -826,7 +830,7 @@ fn type_to_variable<'a>( let mut tag_vars = MutMap::default(); let ext = mempool.get(*ext_id); - for (_tag, tag_argument_types) in tags.iter(mempool) { + for (tag, tag_argument_types) in tags.iter(mempool) { let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len()); for arg_type in tag_argument_types.iter(mempool) { @@ -836,7 +840,7 @@ fn type_to_variable<'a>( } tag_vars.insert( - roc_module::ident::TagName::Private(Symbol::NUM_NUM), + roc_module::ident::TagName::Global(tag.as_str(mempool).into()), tag_argument_vars, ); } diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index 8faa4bcacb..f162f3e577 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -238,3 +238,15 @@ fn constrain_list_of_records() { "List { x : Num * }", ) } + +#[test] +fn constrain_global_tag() { + infer_eq( + indoc!( + r#" + Foo + "# + ), + "[ Foo ]*", + ) +} diff --git a/roc-for-elm-programmers.md b/roc-for-elm-programmers.md index d9850d63e4..bad83663cd 100644 --- a/roc-for-elm-programmers.md +++ b/roc-for-elm-programmers.md @@ -209,7 +209,7 @@ In Elm: In Roc: ``` -{ x : name : Str, email : Str }* -> Str +{ name : Str, email : Str }* -> Str ``` Here, the open record's type variable appears immediately after the `}`.