diff --git a/TUTORIAL.md b/TUTORIAL.md index e412fc87bd..010930e15d 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -492,20 +492,20 @@ This different looking argument is an implicit argument which means that you do Since searching all possible bindings currently in scope would introduce to many ambiguity errors the compiler does not search all bindings when trying to determine an implicit argument. Instead, whether a binding is considered for implicit resolution is controlled by the `@implicit` attribute. When marking a `let` binding as `@implicit` and this binding is in scope it will be considered as a candidate for all implicit arguments. The `@implicit` attribute can also be set on a `type` binding in which case it applied to all `let` bindings which has the type declared by the `type` binding. -```f# +```f#,rust /// @implicit type Test = | Test () let f y: [a] -> a -> a = y let i = Test () // `i` gets selected as the implicit argument since `@implicit` is marked on the type and `i : Test` -f (Test ()) +() ``` #### Passing implicit arguments explicitly If you only use implicit functions as explained above then it might just seem like a different name for traits (Rust) or type classes (Haskell). While it is true that the main reason for implicit arguments is to emulate traits/type classes implicit arguments is more powerful than those approaches as it is also possible to override the implicit resolution and instead give the argument explicitly by prefixing the argument with `?`. -```f# +```f#,rust let list @ { List } = import! std.list // Make a custom equality function which returns true regardless of the elements of the list let { (==) = (===) } = list.eq ?{ (==) = \x y -> True } diff --git a/base/src/ast.rs b/base/src/ast.rs index c6dc0d365c..6ce956e4a5 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -242,7 +242,7 @@ pub struct Array { #[derive(Clone, PartialEq, Debug)] pub struct Lambda { pub id: TypedIdent, - pub args: Vec>, + pub args: Vec>, pub body: Box>, } @@ -356,6 +356,13 @@ impl Argument { name, } } + + pub fn implicit(name: SpannedIdent) -> Self { + Argument { + arg_type: ArgType::Implicit, + name, + } + } } #[derive(Clone, PartialEq, Debug)] @@ -519,7 +526,7 @@ pub fn walk_mut_expr(v: &mut V, e: &mut SpannedExpr { v.visit_ident(&mut lambda.id); for arg in &mut lambda.args { - v.visit_spanned_typed_ident(arg); + v.visit_spanned_typed_ident(&mut arg.name); } v.visit_expr(&mut lambda.body); } diff --git a/check/src/rename.rs b/check/src/rename.rs index 65e51ca749..a995612e6a 100644 --- a/check/src/rename.rs +++ b/check/src/rename.rs @@ -325,14 +325,16 @@ where typ: typ.clone(), }, ); - lambda.args.insert(implicit_arg_count, arg); + lambda + .args + .insert(implicit_arg_count, ast::Argument::implicit(arg)); implicit_arg_count += 1; } for (typ, arg) in types::arg_iter(iter.typ).zip(&mut lambda.args[implicit_arg_count..]) { - arg.value.name = - self.stack_var(arg.value.name.clone(), expr.span, typ.clone()); + arg.name.value.name = + self.stack_var(arg.name.value.name.clone(), expr.span, typ.clone()); } self.visit_expr(&mut lambda.body); diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 6169241d6b..28fc7f9a88 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -9,9 +9,9 @@ use std::mem; use itertools::Itertools; use base::scoped_map::ScopedMap; -use base::ast::{DisplayEnv, Do, Expr, Literal, MutVisitor, Pattern, PatternField, SpannedExpr}; -use base::ast::{AstType, SpannedIdent, SpannedPattern, TypeBinding, Typed, TypedIdent, - ValueBinding}; +use base::ast::{Argument, AstType, DisplayEnv, Do, Expr, Literal, MutVisitor, Pattern, + PatternField, SpannedExpr, SpannedIdent, SpannedPattern, TypeBinding, Typed, + TypedIdent, ValueBinding}; use base::error::Errors; use base::fnv::{FnvMap, FnvSet}; use base::metadata::{Metadata, MetadataEnv}; @@ -925,8 +925,12 @@ impl<'a> Typecheck<'a> { .cloned() .unwrap_or_else(|| self.subs.new_var()); - let mut args = lambda.args.iter_mut().map(|arg| (ArgType::Explicit, arg)); - let mut typ = self.typecheck_lambda(function_type, &mut args, &mut lambda.body); + let mut typ = self.typecheck_lambda( + function_type, + expr.span.start, + &mut lambda.args, + &mut lambda.body, + ); self.generalize_type(level, &mut typ); lambda.id.typ = typ.clone(); @@ -1252,7 +1256,8 @@ impl<'a> Typecheck<'a> { fn typecheck_lambda<'i>( &mut self, mut function_type: ArcType, - args: &mut Iterator)>, + before_args_pos: BytePos, + args: &mut Vec>, body: &mut SpannedExpr, ) -> ArcType { self.enter_scope(); @@ -1263,7 +1268,6 @@ impl<'a> Typecheck<'a> { let mut return_type = function_type.clone(); let mut iter1 = function_arg_iter(self, function_type); - let mut args = args.peekable(); let mut next_type_arg = iter1.next(); let make_new_arg = |tc: &mut Self, span: Span, typ: &mut ArcType| { @@ -1280,7 +1284,9 @@ impl<'a> Typecheck<'a> { arg }; - while args.peek().is_some() || next_type_arg.is_some() { + let mut i = 0; + + while i < args.len() || next_type_arg.is_some() { let (type_implicit, arg_type) = match next_type_arg.take() { Some((type_implicit, arg_type)) => (type_implicit, Some(arg_type)), None => (ArgType::Explicit, None), @@ -1289,9 +1295,34 @@ impl<'a> Typecheck<'a> { match type_implicit { ArgType::Implicit => { let arg_type = arg_type.unwrap(); - let id = match args.peek().map(|t| t.0) { - Some(ArgType::Implicit) => args.next().unwrap().1.value.name.clone(), - _ => Symbol::from(format!("implicit_arg")), + let id = match args.get(i).map(|t| t.arg_type) { + Some(ArgType::Implicit) => { + i += 1; + args[i - 1].name.value.name.clone() + } + _ => { + let id = Symbol::from(format!("implicit_arg")); + let pos = if i == 0 { + before_args_pos + } else { + args.get(i - 1) + .map(|arg| arg.name.span.end) + .unwrap_or(before_args_pos) + }; + args.insert( + i, + Argument::implicit(pos::spanned2( + pos, + pos, + TypedIdent { + typ: arg_type.clone(), + name: id.clone(), + }, + )), + ); + i += 1; + id + } }; arg_types.push(arg_type.clone()); iter1.tc.implicit_resolver.add_implicits_of_record( @@ -1301,8 +1332,12 @@ impl<'a> Typecheck<'a> { ); iter1.tc.stack_var(id, arg_type.clone()); } - ArgType::Explicit => match args.next() { - Some((ArgType::Implicit, arg)) => { + ArgType::Explicit => match args.get_mut(i) { + Some(&mut Argument { + arg_type: ArgType::Implicit, + name: ref arg, + }) => { + i += 1; iter1.tc.error( arg.span, TypeError::Message(format!( @@ -1310,7 +1345,11 @@ impl<'a> Typecheck<'a> { )), ); } - Some((ArgType::Explicit, arg)) => { + Some(&mut Argument { + arg_type: ArgType::Explicit, + name: ref mut arg, + }) => { + i += 1; let arg_type = arg_type.unwrap_or_else(|| { make_new_arg(iter1.tc, arg.span, &mut iter1.typ) }); @@ -1650,15 +1689,17 @@ impl<'a> Typecheck<'a> { } let typ = self.new_skolem_scope_signature(&bind.resolved_type); - self.typecheck_lambda(typ, &mut None.into_iter(), &mut bind.expr) + self.typecheck_lambda(typ, bind.name.span.end, &mut bind.args, &mut bind.expr) } else { let typ = self.new_skolem_scope_signature(&bind.resolved_type); let function_type = self.skolemize(&typ); - let mut args = bind.args - .iter_mut() - .map(|arg| (arg.arg_type, &mut arg.name)); - self.typecheck_lambda(function_type, &mut args, &mut bind.expr) + self.typecheck_lambda( + function_type, + bind.name.span.end, + &mut bind.args, + &mut bind.expr, + ) }; debug!("let {:?} : {}", bind.name, typ); diff --git a/completion/src/lib.rs b/completion/src/lib.rs index a4c80d2914..163e2885b7 100644 --- a/completion/src/lib.rs +++ b/completion/src/lib.rs @@ -587,16 +587,16 @@ where } Expr::Lambda(ref lambda) => { for arg in &lambda.args { - self.on_found.on_ident(&arg.value); + self.on_found.on_ident(&arg.name.value); } - let selection = self.select_spanned(&lambda.args, |arg| arg.span); + let selection = self.select_spanned(&lambda.args, |arg| arg.name.span); match selection { (false, Some(arg)) => { self.found = MatchState::Found(Match::Ident( - arg.span, - &arg.value.name, - &arg.value.typ, + arg.name.span, + &arg.name.value.name, + &arg.name.value.typ, )); } _ => self.visit_expr(&lambda.body), diff --git a/format/src/pretty_print.rs b/format/src/pretty_print.rs index 66fbd313f9..4a3ea07f86 100644 --- a/format/src/pretty_print.rs +++ b/format/src/pretty_print.rs @@ -380,7 +380,7 @@ where let decl = chain![arena; "\\", arena.concat(lambda.args.iter().map(|arg| { - arena.text(arg.value.name.as_ref()).append(" ") + arena.text(arg.name.value.name.as_ref()).append(" ") })), "->" ]; diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index 77f3efb6d1..0a0eb2192b 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -538,10 +538,17 @@ AppExpr = { Expr::App { func: Box::new(expr), implicit_args, args }, }; + +LambdaArgument: Argument = { + => { + Argument::explicit(name) + } +}; + InfixExpr = { AppExpr, - "\\" "->" => + "\\" "->" => Expr::Lambda(Lambda { id: new_ident(type_cache, env.from_str("")), args, diff --git a/parser/tests/support/mod.rs b/parser/tests/support/mod.rs index 193d0abdcc..49481e70f3 100644 --- a/parser/tests/support/mod.rs +++ b/parser/tests/support/mod.rs @@ -196,7 +196,7 @@ pub fn lambda(name: &str, args: Vec, body: SpExpr) -> SpExpr { no_loc(Expr::Lambda(Lambda { id: TypedIdent::new(intern(name)), args: args.into_iter() - .map(|id| no_loc(TypedIdent::new(id))) + .map(|id| Argument::explicit(no_loc(TypedIdent::new(id)))) .collect(), body: Box::new(body), })) diff --git a/tests/vm.rs b/tests/vm.rs index 6e87f01b64..f3296259b7 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -471,6 +471,17 @@ r#" true } +test_expr!{ implicit_argument_selection, +r#" +/// @implicit +type Test = | Test () +let f y: [a] -> a -> () = () +let i = Test () +f (Test ()) +"#, +() +} + #[test] fn rename_types_after_binding() { let _ = ::env_logger::try_init(); diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index 726e931ac8..7bfd7b9afd 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -588,7 +588,11 @@ impl<'a, 'e> Translator<'a, 'e> { ast::Expr::Lambda(ref lambda) => self.new_lambda( expr.span.start, lambda.id.clone(), - lambda.args.iter().map(|arg| arg.value.clone()).collect(), + lambda + .args + .iter() + .map(|arg| arg.name.value.clone()) + .collect(), self.translate_alloc(&lambda.body), expr.span, ),