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:
parent
b0e6418161
commit
b3767071a8
@ -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).
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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.">>,
|
||||||
|
14
test/contracts/operator_lambdas.aes
Normal file
14
test/contracts/operator_lambdas.aes
Normal 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
|
Loading…
x
Reference in New Issue
Block a user