Allow binary operators to be used as lambdas (#385)

* Add operator lambdas

* Do not register anonymous functions as called functions

* Add tests

* Update CHANGELOG

* Update the docs

* Do not allow (..) to be used as a lambda

* Rename the function sum to any
This commit is contained in:
Gaith Hallak 2022-06-03 13:12:23 +04:00 committed by GitHub
parent b0e6418161
commit b3767071a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 2 deletions

View File

@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
``` ```
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3])) [1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
``` ```
- Allow binary operators to be used as lambdas
```
function sum(l : list(int)) : int = foldl((+), 0, l)
function logical_and(x, y) = (&&)(x, y)
```
### Changed ### Changed
- Error messages have been restructured (less newlines) to provide more unified errors. Also `pp_oneline/1` has been added. - Error messages have been restructured (less newlines) to provide more unified errors. Also `pp_oneline/1` has been added.
- Ban empty record definitions (e.g. `record r = {}` would give an error). - Ban empty record definitions (e.g. `record r = {}` would give an error).

View File

@ -200,6 +200,7 @@ switch(f(x))
```c ```c
Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1 Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1
| '(' BinOp ')' // Operator lambda (+)
| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x | 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x
| Expr ':' Type // Type annotation 5 : int | Expr ':' Type // Type annotation 5 : int
| Expr BinOp Expr // Binary operator x + y | Expr BinOp Expr // Binary operator x + y

View File

@ -1641,7 +1641,10 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
GeneralResultType = fresh_uvar(Ann), GeneralResultType = fresh_uvar(Ann),
ResultType = fresh_uvar(Ann), ResultType = fresh_uvar(Ann),
when_warning(warn_unused_functions, when_warning(warn_unused_functions,
fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), fun() -> if element(1, Name) == lam -> ok;
true -> register_function_call(Namespace ++ qname(CurrentFun), Name)
end
end),
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
add_constraint( add_constraint(

View File

@ -335,7 +335,7 @@ exprAtom() ->
?LAZY_P(begin ?LAZY_P(begin
Expr = ?LAZY_P(expr()), Expr = ?LAZY_P(expr()),
choice( choice(
[ id_or_addr(), con(), token(qid), token(qcon) [ id_or_addr(), con(), token(qid), token(qcon), binop_as_lam()
, token(bytes), token(string), token(char) , token(bytes), token(string), token(char)
, token(int) , token(int)
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int))) , ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
@ -472,6 +472,19 @@ id() -> token(id).
tvar() -> token(tvar). tvar() -> token(tvar).
str() -> token(string). str() -> token(string).
binop_as_lam() ->
BinOps = ['&&', '||',
'+', '-', '*', '/', '^', 'mod',
'==', '!=', '<', '>', '<=', '=<', '>=',
'::', '++', '|>'],
OpToLam = fun(Op = {_, Ann}) ->
IdL = {id, Ann, "l"},
IdR = {id, Ann, "r"},
Arg = fun(Id) -> {arg, Ann, Id, type_wildcard(Ann)} end,
{lam, Ann, [Arg(IdL), Arg(IdR)], infix(IdL, Op, IdR)}
end,
?RULE(parens(choice(lists:map(fun token/1, BinOps))), OpToLam(_1)).
token(Tag) -> token(Tag) ->
?RULE(tok(Tag), ?RULE(tok(Tag),
case _1 of case _1 of

View File

@ -795,6 +795,14 @@ failing_contracts() ->
[<<?Pos(2,5) [<<?Pos(2,5)
"Empty record definitions are not allowed. Cannot define the record `r`">> "Empty record definitions are not allowed. Cannot define the record `r`">>
]) ])
, ?TYPE_ERROR(operator_lambdas,
[<<?Pos(9,32)
"Cannot unify `(int, int) => int` and `(int) => 'a`\n"
"when checking the application of\n"
" `(l : _, r : _) => l + r : (int, int) => int`\n"
"to arguments\n"
" `1 : int`">>
])
, ?TYPE_ERROR(warnings, , ?TYPE_ERROR(warnings,
[<<?Pos(0, 0) [<<?Pos(0, 0)
"The file `Triple.aes` is included but not used.">>, "The file `Triple.aes` is included but not used.">>,

View File

@ -0,0 +1,14 @@
include "List.aes"
contract C =
type state = int
function any(l : list(bool)) : bool = List.foldl((||), false, l)
entrypoint init() =
let bad_application = (+)(1)
let good_application = (-)(3, 4)
let op_var = (+)
let op_var_application = op_var(3, 4)
good_application + op_var_application