Compare commits

...

5 Commits

Author SHA1 Message Date
radrow a5cbf2fd79 Updated CHANGELOG 2020-03-23 12:48:47 +01:00
Radosław Rowicki 83e03f3013 Added documentation (#239)
* Added documentation

* Update readme

* Update readme

* Format fix

* Events

* Stdlib mention

* Frac doc

* Frac doc comparison warning

* Typos

* Format fix, TOC added

* Fixed link

* Update editor message

* Split TOC

* Moved out AEVM ABI

* Minor format

Co-Authored-By: Hans Svensson <hanssv@gmail.com>

* Typo

Co-Authored-By: Hans Svensson <hanssv@gmail.com>

* Grammar

Co-Authored-By: Hans Svensson <hanssv@gmail.com>

* Language

Co-authored-by: Hans Svensson <hanssv@gmail.com>
2020-03-10 12:39:39 +01:00
Radosław Rowicki d7fa4d65ec More comments in stdlib (#237) 2020-02-25 12:56:51 +01:00
Radosław Rowicki bd7ed2ef8c Instant unification error on arguments count mismatch (#225)
* Instant unification error on arguments count mismatch

* add testcase

* Add newline
2020-02-21 10:28:55 +01:00
Radosław Rowicki 2bf65cfd98 Add Frac (#222)
Fix bugs in Frac

Added optimizer
2020-02-13 11:02:47 +01:00
13 changed files with 3252 additions and 26 deletions
+5
View File
@@ -6,7 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Added documentation (actually, moved from `protocol`)
- Added standard library docs. Moved builtin docs from `sophia.md` to
`sophia_stdlib.md`
- Added library for rational numbers
### Changed ### Changed
- Optimized `List.aes` library
### Removed ### Removed
## [4.2.0] - 2020-01-15 ## [4.2.0] - 2020-01-15
+9 -2
View File
@@ -2,13 +2,19 @@
This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code. This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code.
For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md).
It is an OTP application written in Erlang and is by default included in It is an OTP application written in Erlang and is by default included in
[the æternity node](https://github.com/aeternity/epoch). However, it can [the æternity node](https://github.com/aeternity/epoch). However, it can
also be included in other systems to compile contracts coded in sophia which also be included in other systems to compile contracts coded in sophia which
can then be loaded into the æternity system. can then be loaded into the æternity system.
## Documentation
* [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
* [Sophia Documentation](docs/sophia.md).
* [Sophia Standard Library](docs/sophia_stdlib.md).
## Versioning ## Versioning
`aesophia` has a version that is only loosely connected to the version of the `aesophia` has a version that is only loosely connected to the version of the
@@ -17,6 +23,7 @@ minor/patch version. The `aesophia` compiler version MUST be bumped whenever
there is a change in how byte code is generated, but it MAY also be bumped upon there is a change in how byte code is generated, but it MAY also be bumped upon
API changes etc. API changes etc.
## Interface Modules ## Interface Modules
The basic modules for interfacing the compiler: The basic modules for interfacing the compiler:
+1064
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+176
View File
@@ -0,0 +1,176 @@
namespace Frac =
private function gcd(a : int, b : int) =
if (b == 0) a else gcd(b, a mod b)
private function abs_int(a : int) = if (a < 0) -a else a
datatype frac = Pos(int, int) | Zero | Neg(int, int)
/** Checks if the internal representation is correct.
* Numerator and denominator must be positive.
* Exposed for debug purposes
*/
function is_sane(f : frac) : bool = switch(f)
Pos(n, d) => n > 0 && d > 0
Zero => true
Neg(n, d) => n > 0 && d > 0
function num(f : frac) : int = switch(f)
Pos(n, _) => n
Neg(n, _) => -n
Zero => 0
function den(f : frac) : int = switch(f)
Pos(_, d) => d
Neg(_, d) => d
Zero => 1
function to_pair(f : frac) : int * int = switch(f)
Pos(n, d) => (n, d)
Neg(n, d) => (-n, d)
Zero => (0, 1)
function sign(f : frac) : int = switch(f)
Pos(_, _) => 1
Neg(_, _) => -1
Zero => 0
function to_str(f : frac) : string = switch(f)
Pos(n, d) => String.concat(Int.to_str(n), if (d == 1) "" else String.concat("/", Int.to_str(d)))
Neg(n, d) => String.concat("-", to_str(Pos(n, d)))
Zero => "0"
/** Reduce fraction to normal form
*/
function simplify(f : frac) : frac =
switch(f)
Neg(n, d) =>
let cd = gcd(n, d)
Neg(n / cd, d / cd)
Zero => Zero
Pos(n, d) =>
let cd = gcd(n, d)
Pos(n / cd, d / cd)
/** Integer to rational division
*/
function make_frac(n : int, d : int) : frac =
if (d == 0) abort("Division by zero")
elif (n == 0) Zero
elif ((n < 0) == (d < 0)) simplify(Pos(abs_int(n), abs_int(d)))
else simplify(Neg(abs_int(n), abs_int(d)))
function eq(a : frac, b : frac) : bool =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
(na == nb && da == db) || na * db == nb * da // they are more likely to be normalized
function neq(a : frac, b : frac) : bool =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
(na != nb || da != db) && na * db != nb * da
function geq(a : frac, b : frac) : bool = num(a) * den(b) >= num(b) * den(a)
function leq(a : frac, b : frac) : bool = num(a) * den(b) =< num(b) * den(a)
function gt(a : frac, b : frac) : bool = num(a) * den(b) > num(b) * den(a)
function lt(a : frac, b : frac) : bool = num(a) * den(b) < num(b) * den(a)
function min(a : frac, b : frac) : frac = if (leq(a, b)) a else b
function max(a : frac, b : frac) : frac = if (geq(a, b)) a else b
function abs(f : frac) : frac = switch(f)
Pos(n, d) => Pos(n, d)
Zero => Zero
Neg(n, d) => Pos(n, d)
function from_int(n : int) : frac =
if (n > 0) Pos(n, 1)
elif (n < 0) Neg(-n, 1)
else Zero
function floor(f : frac) : int = switch(f)
Pos(n, d) => n / d
Zero => 0
Neg(n, d) => -(n + d - 1) / d
function ceil(f : frac) : int = switch(f)
Pos(n, d) => (n + d - 1) / d
Zero => 0
Neg(n, d) => -n / d
function round_to_zero(f : frac) : int = switch(f)
Pos(n, d) => n / d
Zero => 0
Neg(n, d) => -n / d
function round_from_zero(f : frac) : int = switch(f)
Pos(n, d) => (n + d - 1) / d
Zero => 0
Neg(n, d) => -(n + d - 1) / d
/** Round towards nearest integer. If two integers are in the same
* distance, choose the even one.
*/
function round(f : frac) : int =
let fl = floor(f)
let cl = ceil(f)
let dif_fl = abs(sub(f, from_int(fl)))
let dif_cl = abs(sub(f, from_int(cl)))
if (gt(dif_fl, dif_cl)) cl
elif (gt(dif_cl, dif_fl)) fl
elif (fl mod 2 == 0) fl
else cl
function add(a : frac, b : frac) : frac =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
if (da == db) make_frac(na + nb, da)
else make_frac(na * db + nb * da, da * db)
function neg(a : frac) : frac = switch(a)
Neg(n, d) => Pos(n, d)
Zero => Zero
Pos(n, d) => Neg(n, d)
function sub(a : frac, b : frac) : frac = add(a, neg(b))
function inv(a : frac) : frac = switch(a)
Neg(n, d) => Neg(d, n)
Zero => abort("Inversion of zero")
Pos(n, d) => Pos(d, n)
function mul(a : frac, b : frac) : frac = make_frac(num(a) * num(b), den(a) * den(b))
function div(a : frac, b : frac) : frac = mul(a, inv(b))
/** `b` to the power of `e`
*/
function int_exp(b : frac, e : int) : frac =
if (sign(b) == 0 && e == 0) abort("Zero to the zero exponentation")
elif (e < 0) inv(int_exp_(b, -e))
else int_exp_(b, e)
private function int_exp_(b : frac, e : int) =
if (e == 0) from_int(1)
elif (e == 1) b
else
let half = int_exp_(b, e / 2)
if (e mod 2 == 1) mul(mul(half, half), b)
else mul(half, half)
/** Reduces the fraction's in-memory size by dividing its components by two until the
* the error is bigger than `loss` value
*/
function optimize(f : frac, loss : frac) : frac =
require(geq(loss, Zero), "negative loss optimize")
let s = sign(f)
mul(from_int(s), run_optimize(abs(f), loss))
private function run_optimize(f : frac, loss : frac) : frac =
let t = make_frac((num(f) + 1) / 2, (den(f) + 1)/2)
if(gt(abs(sub(t, f)), loss)) f
elif (eq(t, f)) f
else run_optimize(t, loss)
+41 -10
View File
@@ -12,35 +12,66 @@ namespace Func =
function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) function rapply(x : 'a, f : 'a => 'b) : 'b = f(x)
/* The Z combinator - replacement for local and anonymous recursion. /** The Z combinator - replacement for local and anonymous recursion.
*/ */
function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res =
(x) => f(recur(f), x) (x) => f(recur(f), x)
/** n-times composition with itself
*/
function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x)
private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a =
if(n == 0) acc if(n == 0) acc
elif(n == 1) comp(f, acc) elif(n == 1) comp(f, acc)
else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc))
function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = /** Turns an ugly, bad and disgusting arity-n function into
* a beautiful and sweet function taking the first argument
* and returning a function watiting for the remaining ones
* in the same manner
*/
function curry2(f : ('a, 'b) => 'x) : 'a => ('b => 'x) =
(x) => (y) => f(x, y) (x) => (y) => f(x, y)
function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = function curry3(f : ('a, 'b, 'c) => 'x) : 'a => ('b => ('c => 'x)) =
(x) => (y) => (z) => f(x, y, z) (x) => (y) => (z) => f(x, y, z)
function curry4(f : ('a, 'b, 'c, 'd) => 'x) : 'a => ('b => ('c => ('d => 'x))) =
(x) => (y) => (z) => (w) => f(x, y, z, w)
function curry5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a => ('b => ('c => ('d => ('e => 'x)))) =
(x) => (y) => (z) => (w) => (q) => f(x, y, z, w, q)
function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = /** Opposite of curry. Gross
*/
function uncurry2(f : 'a => ('b => 'x)) : ('a, 'b) => 'x =
(x, y) => f(x)(y) (x, y) => f(x)(y)
function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = function uncurry3(f : 'a => ('b => ('c => 'x))) : ('a, 'b, 'c) => 'x =
(x, y, z) => f(x)(y)(z) (x, y, z) => f(x)(y)(z)
function uncurry4(f : 'a => ('b => ('c => ('d => 'x)))) : ('a, 'b, 'c, 'd) => 'x =
(x, y, z, w) => f(x)(y)(z)(w)
function uncurry5(f : 'a => ('b => ('c => ('d => ('e => 'x))))) : ('a, 'b, 'c, 'd, 'e) => 'x =
(x, y, z, w, q) => f(x)(y)(z)(w)(q)
function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = /** Turns an arity-n function into a function taking n-tuple
*/
function tuplify2(f : ('a, 'b) => 'x) : (('a * 'b)) => 'x =
(t) => switch(t) (t) => switch(t)
(x, y) => f(x, y) (x, y) => f(x, y)
function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = function tuplify3(f : ('a, 'b, 'c) => 'x) : 'a * 'b * 'c => 'x =
(t) => switch(t) (t) => switch(t)
(x, y, z) => f(x, y, z) (x, y, z) => f(x, y, z)
function tuplify4(f : ('a, 'b, 'c, 'd) => 'x) : 'a * 'b * 'c * 'd => 'x =
(t) => switch(t)
(x, y, z, w) => f(x, y, z, w)
function tuplify5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a * 'b * 'c * 'd * 'e => 'x =
(t) => switch(t)
(x, y, z, w, q) => f(x, y, z, w, q)
function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = /** Opposite of tuplify
*/
function untuplify2(f : 'a * 'b => 'x) : ('a, 'b) => 'x =
(x, y) => f((x, y)) (x, y) => f((x, y))
function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = function untuplify3(f : 'a * 'b * 'c => 'x) : ('a, 'b, 'c) => 'x =
(x, y, z) => f((x, y, z)) (x, y, z) => f((x, y, z))
function untuplify4(f : 'a * 'b * 'c * 'd => 'x) : ('a, 'b, 'c, 'd) => 'x =
(x, y, z, w) => f((x, y, z, w))
function untuplify5(f : 'a * 'b * 'c * 'd * 'e => 'x) : ('a, 'b, 'c, 'd, 'e) => 'x =
(x, y, z, w, q) => f((x, y, z, w, q))
+44 -11
View File
@@ -19,10 +19,15 @@ namespace List =
[x] => Some(x) [x] => Some(x)
_::t => last(t) _::t => last(t)
/** Finds first element of `l` fulfilling predicate `p` as `Some` or `None`
* if no such element exists.
*/
function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) function find(p : 'a => bool, l : list('a)) : option('a) = switch(l)
[] => None [] => None
h::t => if(p(h)) Some(h) else find(p, t) h::t => if(p(h)) Some(h) else find(p, t)
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
*/
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, [])
private function find_indices_( p : 'a => bool private function find_indices_( p : 'a => bool
, l : list('a) , l : list('a)
@@ -50,14 +55,22 @@ namespace List =
_::t => length_(t, acc + 1) _::t => length_(t, acc + 1)
/** Creates an ascending sequence of all integer numbers
* between `a` and `b` (including `a` and `b`)
*/
function from_to(a : int, b : int) : list(int) = [a..b] function from_to(a : int, b : int) : list(int) = [a..b]
/** Creates an ascending sequence of integer numbers betweeen
* `a` and `b` jumping by given `step`. Includes `a` and takes
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
*/
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
private function from_to_step_(a, b, s, acc) = private function from_to_step_(a, b, s, acc) =
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc) if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
/* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ /** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
*/
function replace_at(n : int, e : 'a, l : list('a)) : list('a) = function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, []) if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, [])
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
@@ -66,7 +79,8 @@ namespace List =
h::t => if (n == 0) reverse(e::acc) ++ t h::t => if (n == 0) reverse(e::acc) ++ t
else replace_at_(n-1, e, t, h::acc) else replace_at_(n-1, e, t, h::acc)
/* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ /** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
*/
function insert_at(n : int, e : 'a, l : list('a)) : list('a) = function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, []) if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, [])
private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
@@ -75,6 +89,9 @@ namespace List =
[] => abort("insert_at overflow") [] => abort("insert_at overflow")
h::t => insert_at_(n-1, e, t, h::acc) h::t => insert_at_(n-1, e, t, h::acc)
/** Assuming that cmp represents `<` comparison, inserts `x` before
* the first element in the list `l` which is greater than it
*/
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
insert_by_(cmp, x, l, []) insert_by_(cmp, x, l, [])
private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) = private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) =
@@ -109,6 +126,8 @@ namespace List =
[] => reverse(acc) [] => reverse(acc)
h::t => map_(f, t, f(h)::acc) h::t => map_(f, t, f(h)::acc)
/** Effectively composition of `map` and `flatten`
*/
function flat_map(f : 'a => list('b), l : list('a)) : list('b) = function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
ListInternal.flat_map(f, l) ListInternal.flat_map(f, l)
@@ -117,7 +136,8 @@ namespace List =
[] => reverse(acc) [] => reverse(acc)
h::t => filter_(p, t, if(p(h)) h::acc else acc) h::t => filter_(p, t, if(p(h)) h::acc else acc)
/* Take `n` first elements */ /** Take `n` first elements
*/
function take(n : int, l : list('a)) : list('a) = function take(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Take negative number of elements") else take_(n, l, []) if(n < 0) abort("Take negative number of elements") else take_(n, l, [])
private function take_(n : int, l : list('a), acc : list('a)) : list('a) = private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
@@ -126,7 +146,8 @@ namespace List =
[] => reverse(acc) [] => reverse(acc)
h::t => take_(n-1, t, h::acc) h::t => take_(n-1, t, h::acc)
/* Drop `n` first elements */ /** Drop `n` first elements
*/
function drop(n : int, l : list('a)) : list('a) = function drop(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Drop negative number of elements") if(n < 0) abort("Drop negative number of elements")
elif (n == 0) l elif (n == 0) l
@@ -134,18 +155,23 @@ namespace List =
[] => [] [] => []
h::t => drop(n-1, t) h::t => drop(n-1, t)
/* Get the longest prefix of a list in which every element matches predicate `p` */ /** Get the longest prefix of a list in which every element
* matches predicate `p`
*/
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc) [] => reverse(acc)
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
/* Drop elements from `l` until `p` holds */ /** Drop elements from `l` until `p` holds
*/
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
[] => [] [] => []
h::t => if(p(h)) drop_while(p, t) else l h::t => if(p(h)) drop_while(p, t) else l
/* Splits list into two lists of elements that respectively match and don't match predicate `p` */ /** Splits list into two lists of elements that respectively
* match and don't match predicate `p`
*/
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
private function partition_( p : 'a => bool private function partition_( p : 'a => bool
, l : list('a) , l : list('a)
@@ -155,7 +181,8 @@ namespace List =
[] => (reverse(acc_t), reverse(acc_f)) [] => (reverse(acc_t), reverse(acc_f))
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
/** Flattens list of lists into a single list
*/
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
function all(p : 'a => bool, l : list('a)) : bool = switch(l) function all(p : 'a => bool, l : list('a)) : bool = switch(l)
@@ -171,7 +198,9 @@ namespace List =
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l) function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
/* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ /** Zips two list by applying bimapping function on respective elements.
* Drops longer tail.
*/
function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
private function zip_with_( f : ('a, 'b) => 'c private function zip_with_( f : ('a, 'b) => 'c
, l1 : list('a) , l1 : list('a)
@@ -181,7 +210,8 @@ namespace List =
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
_ => reverse(acc) _ => reverse(acc)
/* Zips two lists into list of pairs. Drops longer tail. */ /** Zips two lists into list of pairs. Drops longer tail.
*/
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
@@ -199,7 +229,8 @@ namespace List =
h::t => switch (partition((x) => lesser_cmp(x, h), t)) h::t => switch (partition((x) => lesser_cmp(x, h), t))
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
/** Puts `delim` between every two members of the list
*/
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc) [] => reverse(acc)
@@ -207,6 +238,8 @@ namespace List =
h::t => intersperse_(delim, t, delim::h::acc) h::t => intersperse_(delim, t, delim::h::acc)
/** Effectively a zip with an infinite sequence of natural numbers
*/
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, [])
private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l)
[] => reverse(acc) [] => reverse(acc)
+20 -2
View File
@@ -10,13 +10,18 @@ namespace Option =
None => false None => false
Some(_) => true Some(_) => true
/** Catamorphism on `option`. Also known as inlined pattern matching.
*/
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
None => n None => n
Some(x) => s(x) Some(x) => s(x)
/** Escape option providing default if `None`
*/
function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o)
/** Assume it is `Some`
*/
function force(o : option('a)) : 'a = default(abort("Forced None value"), o) function force(o : option('a)) : 'a = default(abort("Forced None value"), o)
function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o) function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o)
@@ -40,10 +45,14 @@ namespace Option =
(Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3))
_ => None _ => None
/** Like `map`, but the function is in `option`
*/
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
(Some(ff), Some(xx)) => Some(ff(xx)) (Some(ff), Some(xx)) => Some(ff(xx))
_ => None _ => None
/** Monadic bind
*/
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
None => None None => None
Some(x) => f(x) Some(x) => f(x)
@@ -53,22 +62,31 @@ namespace Option =
None => [] None => []
Some(x) => [x] Some(x) => [x]
/** Turns list of options into a list of elements that are under `Some`s.
* Safe.
*/
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
[] => List.reverse(acc) [] => List.reverse(acc)
None::t => filter_options_(t, acc) None::t => filter_options_(t, acc)
Some(x)::t => filter_options_(t, x::acc) Some(x)::t => filter_options_(t, x::acc)
/** Just like `filter_options` but requires all elements to be `Some` and returns
* None if any of them is not
*/
function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
[] => Some(List.reverse(acc)) [] => Some(List.reverse(acc))
None::t => None None::t => None
Some(x)::t => seq_options_(t, x::acc) Some(x)::t => seq_options_(t, x::acc)
/** Choose `Some` out of two if possible
*/
function choose(o1 : option('a), o2 : option('a)) : option('a) = function choose(o1 : option('a), o2 : option('a)) : option('a) =
if(is_some(o1)) o1 else o2 if(is_some(o1)) o1 else o2
/** Choose `Some` from list of options if possible
*/
function choose_first(l : list(option('a))) : option('a) = switch(l) function choose_first(l : list(option('a))) : option('a) = switch(l)
[] => None [] => None
None::t => choose_first(t) None::t => choose_first(t)
+6
View File
@@ -6,12 +6,18 @@ namespace Pair =
function snd(t : ('a * 'b)) : 'b = switch(t) function snd(t : ('a * 'b)) : 'b = switch(t)
(_, y) => y (_, y) => y
/** Map over first
*/
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
(x, y) => (f(x), y) (x, y) => (f(x), y)
/** Map over second
*/
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
(x, y) => (x, f(y)) (x, y) => (x, f(y))
/** Map over both
*/
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
(x, y) => (f(x), g(y)) (x, y) => (f(x), g(y))
+12
View File
@@ -10,15 +10,23 @@ namespace Triple =
(_, _, z) => z (_, _, z) => z
/** Map over first
*/
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
(x, y, z) => (f(x), y, z) (x, y, z) => (f(x), y, z)
/** Map over second
*/
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
(x, y, z) => (x, f(y), z) (x, y, z) => (x, f(y), z)
/** Map over third
*/
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
(x, y, z) => (x, y, f(z)) (x, y, z) => (x, y, f(z))
/** Map over all elements
*/
function trimap( f : 'a => 'x function trimap( f : 'a => 'x
, g : 'b => 'y , g : 'b => 'y
, h : 'c => 'z , h : 'c => 'z
@@ -29,9 +37,13 @@ namespace Triple =
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
(x, y, z) => (z, y, x) (x, y, z) => (z, y, x)
/** Right rotation
*/
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
(x, y, z) => (z, x, y) (x, y, z) => (z, x, y)
/** Left rotation
*/
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
(x, y, z) => (y, z, x) (x, y, z) => (y, z, x)
+2 -1
View File
@@ -2072,7 +2072,8 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
true; true;
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) -> unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
true; true;
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) -> unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
when length(Args1) == length(Args2) ->
unify(Env, Named1, Named2, When) andalso unify(Env, Named1, Named2, When) andalso
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When); unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When) unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
+22
View File
@@ -623,6 +623,28 @@ failing_contracts() ->
"Empty record/map update\n" "Empty record/map update\n"
" r {}">> " r {}">>
]) ])
, ?TYPE_ERROR(bad_number_of_args,
[<<?Pos(3, 39)
"Cannot unify () => unit\n"
" and (int) => 'a\n",
"when checking the application at line 3, column 39 of\n"
" f : () => unit\n"
"to arguments\n"
" 1 : int">>,
<<?Pos(4, 20)
"Cannot unify (int, string) => 'e\n"
" and (int) => 'd\n"
"when checking the application at line 4, column 20 of\n"
" g : (int, string) => 'e\n"
"to arguments\n"
" 1 : int">>,
<<?Pos(5, 20)
"Cannot unify (int, string) => 'c\n"
" and (string) => 'b\n"
"when checking the application at line 5, column 20 of\n"
" g : (int, string) => 'c\nto arguments\n"
" \"Litwo, ojczyzno moja\" : string">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).
+6
View File
@@ -0,0 +1,6 @@
contract Test =
entrypoint f() = ()
entrypoint g(x : int, y : string) = f(1)
entrypoint h() = g(1)
entrypoint i() = g("Litwo, ojczyzno moja")