feat: Add bidirectional type inference for lambdas
This commit introduces bidirectional type inference, enabling the compiler to infer lambda parameter types based on the context of their usage. This is particularly useful for functions like `pipe`, where a lambda's signature can be deduced from the types of the streams it operates on. Key changes include: - **`extract_lambda_param_hints` function:** This new helper function in `type_checker.rs` is responsible for extracting expected parameter types for a lambda based on the callee's signature and already known argument types. - **`check_lambda_with_param_hints` function:** This function allows type-checking a lambda node using provided parameter type hints, preserving upvalue types from the enclosing scope. - **Call expression type checking:** The `check_call` function now phases its argument type checking. Non-lambda arguments are typed first, and then lambda arguments are typed using hints derived from `extract_lambda_param_hints`. - **`pipe_arg_hint_resolver`:** This new resolver for the `pipe` function's `PolymorphicFn` type allows it to provide specific type hints for its lambda argument based on the types of its stream inputs (single stream, vector of streams, or tuple of streams). - **`StaticType::PolymorphicFn` update:** The `PolymorphicFn` enum variant has been extended to include `resolve_arg_hints`, enabling functions to provide lambda parameter hints during bidirectional type inference. - **New tests:** Added tests to verify the correct inference of lambda parameters for `pipe` with vector and tuple inputs, ensuring that inner stream types are correctly propagated. Also, a test to confirm `pipe` with an optional return still produces `Stream(Float)`.
This commit is contained in:
@@ -1,6 +1,49 @@
|
||||
use myc::ast::environment::Environment;
|
||||
use myc::ast::types::Value;
|
||||
|
||||
/// Verifies that bidirectional type inference correctly propagates stream inner types
|
||||
/// into lambda parameters for `pipe` calls with a single stream in a vector `[src]`.
|
||||
/// The lambda parameter should be inferred as the stream's Record type, not `Any`.
|
||||
#[test]
|
||||
fn test_pipe_infers_lambda_param_from_vector_input() {
|
||||
let env = Environment::new();
|
||||
let source = "(do
|
||||
(def src (create-random-ohlc 42 5))
|
||||
(pipe [src] (fn [tick] (.close tick)))
|
||||
)";
|
||||
let dump = env.dump_ast(source).expect("dump_ast failed");
|
||||
|
||||
// The lambda parameter `tick` should be inferred as the OHLC Record type,
|
||||
// NOT as `Any`. This validates the Vector → Stream<T> → T hint resolution.
|
||||
assert!(
|
||||
!dump.contains("params: Tuple([Any])"),
|
||||
"Lambda params should NOT be Any — bidirectional inference failed.\nDump:\n{dump}"
|
||||
);
|
||||
// The pipe call should return Stream(Float), not Stream(Any)
|
||||
assert!(
|
||||
dump.contains("Stream(Float)"),
|
||||
"Pipe should produce Stream(Float), not Stream(Any).\nDump:\n{dump}"
|
||||
);
|
||||
|
||||
// Also verify it runs correctly
|
||||
let res = env.run_script(source);
|
||||
assert!(res.is_ok(), "Script failed: {:?}", res.err());
|
||||
}
|
||||
|
||||
/// Verifies bidirectional inference for pipe with multiple stream inputs in a tuple.
|
||||
#[test]
|
||||
fn test_pipe_infers_lambda_params_from_tuple_inputs() {
|
||||
let env = Environment::new();
|
||||
let source = "(do
|
||||
(def src1 (create-random-ohlc 42 5))
|
||||
(def src2 (create-random-ohlc 99 5))
|
||||
(pipe [src1 src2] (fn [t1 t2] (+ (.close t1) (.close t2))))
|
||||
)";
|
||||
let res = env.run_script(source);
|
||||
assert!(res.is_ok(), "Script failed: {:?}", res.err());
|
||||
}
|
||||
|
||||
/// Verifies that pipe with Optional return (filter pattern) still produces Stream(Float).
|
||||
#[test]
|
||||
fn test_pipeline_optional_type() {
|
||||
let env = Environment::new();
|
||||
|
||||
Reference in New Issue
Block a user