diff --git a/README.md b/README.md
index f4b0394f..7229e48b 100644
--- a/README.md
+++ b/README.md
@@ -88,9 +88,8 @@ Implemented:
* `val` (including `val rec`)
* `fun` (declare function)
* Operators: `=` `<>` `<` `>` `<=` `>=`
- `~` `+` `-` `*` `/` `div` `mod` `^`
+ `~` `+` `-` `*` `/` `div` `mod` `^` `::` `o` `@`
`andalso` `orelse`
- `::`
* Built-in constants and functions:
`it` `true` `false` `nil` `abs` `not` `ignore`
* Type derivation
@@ -121,7 +120,7 @@ Not implemented:
* `exception`
* `while`
* References, and operators `!` and `:=`
-* Operators: `before` `o`
+* Operators: `before`
* User-defined operators (`infix`, `infixr`)
* Type annotations in expressions and patterns
diff --git a/src/main/java/net/hydromatic/morel/ast/AstBuilder.java b/src/main/java/net/hydromatic/morel/ast/AstBuilder.java
index 33a9b129..c06be9c4 100644
--- a/src/main/java/net/hydromatic/morel/ast/AstBuilder.java
+++ b/src/main/java/net/hydromatic/morel/ast/AstBuilder.java
@@ -260,6 +260,10 @@ public Ast.Exp caret(Ast.Exp a0, Ast.Exp a1) {
return infix(Op.CARET, a0, a1);
}
+ public Ast.Exp o(Ast.Exp a0, Ast.Exp a1) {
+ return infix(Op.COMPOSE, a0, a1);
+ }
+
public Ast.Exp except(Ast.Exp a0, Ast.Exp a1) {
return infix(Op.EXCEPT, a0, a1);
}
diff --git a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java
index e68dd512..61bf4d81 100644
--- a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java
+++ b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java
@@ -170,6 +170,18 @@ public enum BuiltIn {
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.get(0), h.get(0)), h.get(0)))),
+ /** Operator "General.op o", of type "(β → γ) *
+ * (α → β) → α → γ"
+ *
+ *
"f o g" is the function composition of "f" and "g". Thus, "(f o g) a"
+ * is equivalent to "f (g a)". */
+ GENERAL_OP_O("General", "op o", "op o", ts ->
+ ts.forallType(3, h ->
+ ts.fnType(
+ ts.tupleType(ts.fnType(h.get(1), h.get(2)),
+ ts.fnType(h.get(0), h.get(1))),
+ ts.fnType(h.get(0), h.get(2))))),
+
/** Constant "String.maxSize", of type "int".
*
*
"The longest allowed size of a string". */
@@ -311,11 +323,20 @@ public enum BuiltIn {
*
*
"l1 @ l2" returns the list that is the concatenation of l1 and l2.
*/
- // TODO: make this infix "@" rather than prefix "at"
+ // TODO: remove
LIST_AT("List", "at", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.list(0), h.list(0)), h.list(0)))),
+ /** Operator "List.op @", of type "α list * α list → α
+ * list".
+ *
+ *
"l1 @ l2" returns the list that is the concatenation of l1 and l2.
+ */
+ LIST_OP_AT("List", "op @", "op @", ts ->
+ ts.forallType(1, h ->
+ ts.fnType(ts.tupleType(h.list(0), h.list(0)), h.list(0)))),
+
/** Function "List.hd", of type "α list → α".
*
*
"hd l" returns the first element of l. It raises {@code Empty} if l is
diff --git a/src/main/java/net/hydromatic/morel/compile/TypeResolver.java b/src/main/java/net/hydromatic/morel/compile/TypeResolver.java
index ace72109..06e7ca14 100644
--- a/src/main/java/net/hydromatic/morel/compile/TypeResolver.java
+++ b/src/main/java/net/hydromatic/morel/compile/TypeResolver.java
@@ -414,7 +414,9 @@ private Ast.Exp deduceType(TypeEnv env, Ast.Exp node, Unifier.Variable v) {
}
return reg(apply.copy(fn2, arg2), null, v);
+ case AT:
case CARET:
+ case COMPOSE:
case PLUS:
case MINUS:
case TIMES:
diff --git a/src/main/java/net/hydromatic/morel/eval/Codes.java b/src/main/java/net/hydromatic/morel/eval/Codes.java
index 200eb907..c1eb38e3 100644
--- a/src/main/java/net/hydromatic/morel/eval/Codes.java
+++ b/src/main/java/net/hydromatic/morel/eval/Codes.java
@@ -241,6 +241,17 @@ private static int div(EvalEnv env, Object arg) {
return Math.floorDiv((int) list.get(0), (int) list.get(1));
}
+ /** @see BuiltIn#GENERAL_OP_O */
+ private static final Applicable GENERAL_OP_O = Codes::compose;
+
+ /** Implements {@link #GENERAL_OP_O}. */
+ private static Applicable compose(EvalEnv evalEnv, Object arg) {
+ @SuppressWarnings("rawtypes") final List list = (List) arg;
+ final Applicable f = (Applicable) list.get(0);
+ final Applicable g = (Applicable) list.get(1);
+ return (evalEnv2, arg2) -> f.apply(evalEnv2, g.apply(evalEnv2, arg2));
+ }
+
/** @see BuiltIn#OP_CARET */
private static final Applicable OP_CARET = Codes::caret;
@@ -1049,6 +1060,7 @@ public static Applicable aggregate(Environment env, Code aggregateCode,
.put(BuiltIn.NOT, NOT)
.put(BuiltIn.ABS, ABS)
.put(BuiltIn.IGNORE, IGNORE)
+ .put(BuiltIn.GENERAL_OP_O, GENERAL_OP_O)
.put(BuiltIn.OP_CARET, OP_CARET)
.put(BuiltIn.OP_CONS, OP_CONS)
.put(BuiltIn.OP_DIV, OP_DIV)
@@ -1088,6 +1100,7 @@ public static Applicable aggregate(Environment env, Code aggregateCode,
.put(BuiltIn.LIST_NULL, LIST_NULL)
.put(BuiltIn.LIST_LENGTH, LIST_LENGTH)
.put(BuiltIn.LIST_AT, LIST_AT)
+ .put(BuiltIn.LIST_OP_AT, LIST_AT) // op @ == List.at
.put(BuiltIn.LIST_HD, LIST_HD)
.put(BuiltIn.LIST_TL, LIST_TL)
.put(BuiltIn.LIST_LAST, LIST_LAST)
diff --git a/src/main/java/net/hydromatic/morel/util/Folder.java b/src/main/java/net/hydromatic/morel/util/Folder.java
new file mode 100644
index 00000000..f6c1bce9
--- /dev/null
+++ b/src/main/java/net/hydromatic/morel/util/Folder.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to Julian Hyde under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Julian Hyde licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package net.hydromatic.morel.util;
+
+import net.hydromatic.morel.ast.Ast;
+import net.hydromatic.morel.ast.Op;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+import static net.hydromatic.morel.ast.AstBuilder.ast;
+
+/**
+ * Enable creating right-deep trees.
+ *
+ * @param Element type
+ */
+public abstract class Folder {
+ final E e;
+
+ Folder(E e) {
+ this.e = Objects.requireNonNull(e);
+ }
+
+ abstract E combine(List> list);
+
+ public static E combineAll(List> list) {
+ if (list.size() == 0) {
+ throw new AssertionError();
+ }
+ final Folder head = list.get(0);
+ final List> tail = list.subList(1, list.size());
+ return head.combine(tail);
+ }
+
+ private static Folder end(E e) {
+ return new End<>(e);
+ }
+
+ /** Appends an element using "@". */
+ public static void at(List> list, Ast.Exp e) {
+ append(list, e, e1 -> op(e1, Op.AT));
+ }
+
+ /** Appends an element using "::". */
+ public static void cons(List> list, Ast.Exp e) {
+ append(list, e, e1 -> op(e1, Op.CONS));
+ }
+
+ /** Adds an element to an empty list. */
+ public static void start(List> list, E e) {
+ if (!list.isEmpty()) {
+ throw new AssertionError();
+ }
+ list.add(end(e));
+ }
+
+ /** Adds an element and operator to a non-empty list. */
+ private static void append(List> list, E e,
+ Function> fn) {
+ if (list.isEmpty()) {
+ throw new AssertionError();
+ }
+ @SuppressWarnings("unchecked")
+ final End end = (End) list.get(list.size() - 1);
+ list.set(list.size() - 1, fn.apply(end.e));
+ list.add(end(e));
+ }
+
+ /** Creates a folder that combines an expression with whatever follows
+ * using an infix operator. */
+ private static Folder op(Ast.Exp e, final Op at) {
+ return new Folder(e) {
+ Ast.Exp combine(List> list) {
+ final Ast.Exp rest = combineAll(list);
+ return ast.infixCall(e.pos.plus(rest.pos), at, e, rest);
+ }
+ };
+ }
+
+ /** Sub-class of {@code Folder} that marks the end of a list.
+ *
+ * @param element type */
+ private static class End extends Folder {
+ End(E e) {
+ super(e);
+ }
+
+ E combine(List> list) {
+ assert list.isEmpty();
+ return e;
+ }
+ }
+}
+
+// End Folder.java
diff --git a/src/main/javacc/MorelParser.jj b/src/main/javacc/MorelParser.jj
index 731477c8..3b3271b3 100644
--- a/src/main/javacc/MorelParser.jj
+++ b/src/main/javacc/MorelParser.jj
@@ -32,7 +32,7 @@ import net.hydromatic.morel.ast.Ast.*;
import net.hydromatic.morel.ast.AstBuilder;
import net.hydromatic.morel.ast.AstNode;
import net.hydromatic.morel.ast.Pos;
-import net.hydromatic.morel.util.Pair;
+import net.hydromatic.morel.util.Folder;import net.hydromatic.morel.util.Pair;
import com.google.common.collect.ImmutableList;
@@ -581,14 +581,16 @@ Exp expression6() :
Exp expression5() :
{
Exp e;
- final List list = new ArrayList<>();
+ final List> list = new ArrayList<>();
}
{
- e = expression6() { list.add(e); }
+ e = expression6() { Folder.start(list, e); }
(
- e = expression6() { list.add(e); }
+ e = expression6() { Folder.at(list, e); }
+ |
+ e = expression6() { Folder.cons(list, e); }
)*
- { return ast.foldCons(list); }
+ { return Folder.combineAll(list); }
}
/** Parses an expression of precedence level 4 ({@code =}, {@code <>},
@@ -636,6 +638,22 @@ Exp expression4() :
{ return e; }
}
+/** Parses an expression of precedence level 3 (o). */
+Exp expression3() :
+{
+ Exp e;
+ Exp e2;
+}
+{
+ e = expression4()
+ (
+ e2 = expression4() {
+ e = ast.o(e, e2);
+ }
+ )*
+ { return e; }
+}
+
/** Parses an expression of precedence level 2 (andalso). */
Exp expression2() :
{
@@ -643,9 +661,9 @@ Exp expression2() :
Exp e2;
}
{
- e = expression4()
+ e = expression3()
(
- e2 = expression4() {
+ e2 = expression3() {
e = ast.andAlso(e, e2);
}
)*
@@ -1257,6 +1275,7 @@ AstNode statementSemicolon() :
| < LET: "LET" >
| < MOD: "MOD" >
| < NOT_ELEM: "NOTELEM" >
+| < O: "O" >
| < OF: "OF" >
| < ORELSE: "ORELSE" >
| < REC: "REC" >
@@ -1336,6 +1355,7 @@ AstNode statementSemicolon() :
| < SLASH: "/" >
| < TILDE: "~" >
| < CONS: "::" >
+ | < AT: "@" >
| < ELLIPSIS: "..." >
| < QUOTE: "'" >
| < DOUBLE_QUOTE: "\"" >
diff --git a/src/test/java/net/hydromatic/morel/MainTest.java b/src/test/java/net/hydromatic/morel/MainTest.java
index bbc5ff19..65bdf365 100644
--- a/src/test/java/net/hydromatic/morel/MainTest.java
+++ b/src/test/java/net/hydromatic/morel/MainTest.java
@@ -249,6 +249,29 @@ public void describeTo(Description description) {
.assertParse("1 :: 2 :: 3 :: []");
ml("1 + 2 :: 3 + 4 * 5 :: 6").assertParseSame();
+ // o is left-associative;
+ // lower precedence than "=" (4), higher than "andalso" (2)
+ ml("f o g").assertParseSame();
+ ml("f o g o h").assertParseSame();
+ ml("f o (g o h)").assertParseSame();
+ ml("(f o g) o h").assertParse("f o g o h");
+
+ ml("a = f o g andalso c = d").assertParseSame();
+ ml("a = (f o g) andalso (c = d)").assertParse("a = (f o g) andalso c = d");
+ ml("(a = f) o g andalso (c = d)").assertParse("a = f o g andalso c = d");
+
+ // @ is right-associative;
+ // lower precedence than "+" (6), higher than "=" (4)
+ ml("f @ g").assertParseSame();
+ ml("f @ g @ h").assertParseSame();
+ ml("f @ (g @ h)").assertParse("f @ g @ h");
+ ml("(f @ g) @ h").assertParseSame();
+
+ // ^ is left-associative;
+ // lower precedence than "*" (7), higher than "@" (5)
+ ml("a * f ^ g @ b").assertParseSame();
+ ml("(a * f) ^ (g @ b)").assertParse("a * f ^ (g @ b)");
+
ml("(1 + 2, 3, true, (5, 6), 7 = 8)").assertParseSame();
ml("let val x = 2 in x + (3 + x) + x end").assertParseSame();
diff --git a/src/test/java/net/hydromatic/morel/UtilTest.java b/src/test/java/net/hydromatic/morel/UtilTest.java
index 455fe2b2..d8704d82 100644
--- a/src/test/java/net/hydromatic/morel/UtilTest.java
+++ b/src/test/java/net/hydromatic/morel/UtilTest.java
@@ -18,6 +18,9 @@
*/
package net.hydromatic.morel;
+import net.hydromatic.morel.ast.Ast;
+import net.hydromatic.morel.ast.Pos;
+import net.hydromatic.morel.util.Folder;
import net.hydromatic.morel.util.MapList;
import net.hydromatic.morel.util.Ord;
import net.hydromatic.morel.util.TailList;
@@ -28,6 +31,8 @@
import java.util.Arrays;
import java.util.List;
+import static net.hydromatic.morel.ast.AstBuilder.ast;
+
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
@@ -88,6 +93,19 @@ public class UtilTest {
assertThat(abc.get(2), is("c"));
assertThat(String.join(",", abc), is("a,b,c"));
}
+
+ @Test public void testFolder() {
+ final List> list = new ArrayList<>();
+ Folder.start(list, ast.stringLiteral(Pos.ZERO, "a"));
+ Folder.at(list, ast.stringLiteral(Pos.ZERO, "b"));
+ Folder.at(list, ast.stringLiteral(Pos.ZERO, "c"));
+ assertThat(Folder.combineAll(list).toString(), is("\"a\" @ \"b\" @ \"c\""));
+
+ list.clear();
+ Folder.start(list, ast.stringLiteral(Pos.ZERO, "a"));
+ Folder.cons(list, ast.stringLiteral(Pos.ZERO, "b"));
+ assertThat(Folder.combineAll(list).toString(), is("\"a\" :: \"b\""));
+ }
}
// End UtilTest.java
diff --git a/src/test/resources/script/builtIn.sml b/src/test/resources/script/builtIn.sml
index c1c744f8..7f1d86ec 100644
--- a/src/test/resources/script/builtIn.sml
+++ b/src/test/resources/script/builtIn.sml
@@ -17,6 +17,18 @@
* License.
*)
+(* Operators --------------------------------------------------- *)
+
+(*) op o - function composition
+val plusOne = fn x => x + 1;
+val timesTwo = fn x => x * 2;
+val plusThree = fn x => x + 3;
+plusOne o timesTwo;
+(plusOne o timesTwo) 3;
+plusOne o timesTwo o plusThree;
+((plusOne o timesTwo) o plusThree) 3;
+(plusOne o (timesTwo o plusThree)) 3;
+
(* Miscellaneous ----------------------------------------------- *)
ignore;
ignore (1 + 2);
@@ -170,6 +182,9 @@ List.at ([1], []);
List.at ([], [2]);
List.at ([], []);
+[1] @ [2, 3];
+[] @ [];
+
(*) val hd : 'a list -> 'a
List.hd;
List.hd [1,2,3];
diff --git a/src/test/resources/script/builtIn.sml.out b/src/test/resources/script/builtIn.sml.out
index eb3e52d6..5b9f332f 100644
--- a/src/test/resources/script/builtIn.sml.out
+++ b/src/test/resources/script/builtIn.sml.out
@@ -17,6 +17,34 @@
* License.
*)
+(* Operators --------------------------------------------------- *)
+
+(*) op o - function composition
+val plusOne = fn x => x + 1;
+val plusOne = fn : int -> int
+
+val timesTwo = fn x => x * 2;
+val timesTwo = fn : int -> int
+
+val plusThree = fn x => x + 3;
+val plusThree = fn : int -> int
+
+plusOne o timesTwo;
+val it = fn : int -> int
+
+(plusOne o timesTwo) 3;
+val it = 7 : int
+
+plusOne o timesTwo o plusThree;
+val it = fn : int -> int
+
+((plusOne o timesTwo) o plusThree) 3;
+val it = 13 : int
+
+(plusOne o (timesTwo o plusThree)) 3;
+val it = 13 : int
+
+
(* Miscellaneous ----------------------------------------------- *)
ignore;
val it = fn : 'a -> unit
@@ -38,9 +66,9 @@ List;
val it =
{all=fn,app=fn,at=fn,collate=fn,concat=fn,drop=fn,exists=fn,filter=fn,
find=fn,foldl=fn,foldr=fn,getItem=fn,hd=fn,last=fn,length=fn,map=fn,
- mapPartial=fn,nil=[],nth=fn,null=fn,partition=fn,rev=fn,revAppend=fn,
- tabulate=fn,take=fn,tl=fn}
- : {all:('a -> bool) -> 'a list -> bool, app:('b -> unit) -> 'b list -> unit, at:'c list * 'c list -> 'c list, collate:('d * 'd -> int) -> 'd list * 'd list -> int, concat:'e list list -> 'e list, drop:'f list * int -> 'f list, exists:('g -> bool) -> 'g list -> bool, filter:('h -> bool) -> 'h list -> 'h list, find:('i -> bool) -> 'i list -> 'i, foldl:('j * 'k -> 'k) -> 'k -> 'j list -> 'k, foldr:('l * 'm -> 'm) -> 'm -> 'l list -> 'm, getItem:'n list -> 'n * 'n list, hd:'o list -> 'o, last:'p list -> 'p, length:'q list -> int, map:('r -> 's) -> 'r list -> 's list, mapPartial:('t -> 'u) -> 't list -> 'u list, nil:'v list, nth:'w list * int -> 'w, null:'x list -> bool, partition:('y -> bool) -> 'y list -> 'y list * 'y list, rev:'z list -> 'z list, revAppend:'ba list * 'ba list -> 'ba list, tabulate:int * (int -> 'bb) -> 'bb list, take:'bc list * int -> 'bc list, tl:'bd list -> 'bd list}
+ mapPartial=fn,nil=[],nth=fn,null=fn,op @=fn,partition=fn,rev=fn,
+ revAppend=fn,tabulate=fn,take=fn,tl=fn}
+ : {all:('a -> bool) -> 'a list -> bool, app:('b -> unit) -> 'b list -> unit, at:'c list * 'c list -> 'c list, collate:('d * 'd -> int) -> 'd list * 'd list -> int, concat:'e list list -> 'e list, drop:'f list * int -> 'f list, exists:('g -> bool) -> 'g list -> bool, filter:('h -> bool) -> 'h list -> 'h list, find:('i -> bool) -> 'i list -> 'i, foldl:('j * 'k -> 'k) -> 'k -> 'j list -> 'k, foldr:('l * 'm -> 'm) -> 'm -> 'l list -> 'm, getItem:'n list -> 'n * 'n list, hd:'o list -> 'o, last:'p list -> 'p, length:'q list -> int, map:('r -> 's) -> 'r list -> 's list, mapPartial:('t -> 'u) -> 't list -> 'u list, nil:'v list, nth:'w list * int -> 'w, null:'x list -> bool, op @:'y list * 'y list -> 'y list, partition:('z -> bool) -> 'z list -> 'z list * 'z list, rev:'ba list -> 'ba list, revAppend:'bb list * 'bb list -> 'bb list, tabulate:int * (int -> 'bc) -> 'bc list, take:'bd list * int -> 'bd list, tl:'be list -> 'be list}
List.rev;
val it = fn : 'a list -> 'a list
@@ -341,6 +369,13 @@ List.at ([], []);
val it = [] : 'a list
+[1] @ [2, 3];
+val it = [1,2,3] : int list
+
+[] @ [];
+val it = [] : 'a list
+
+
(*) val hd : 'a list -> 'a
List.hd;
val it = fn : 'a list -> 'a
@@ -793,9 +828,9 @@ val it = fn : unit -> (string * string) list
Sys.env ();
val it =
- [
+ [("General","{op o:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"),
("List",
- "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"),
+ "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, op @:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"),
("Relational",
"{count:forall 'a. 'a list -> int, max:forall 'a. 'a list -> 'a, min:forall 'a. 'a list -> 'a, sum:forall 'a. 'a list -> 'a}"),
("String",
@@ -804,8 +839,8 @@ val it =
("count","forall 'a. 'a list -> int"),
("env","unit -> (string * string) list"),("false","bool"),
("ignore","forall 'a. 'a -> unit"),("it","(string * string) list"),
- ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),
- ("max","forall 'a. 'a list -> 'a"),...] : (string * string) list
+ ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),...]
+ : (string * string) list
env;
@@ -813,9 +848,9 @@ val it = fn : unit -> (string * string) list
env ();
val it =
- [
+ [("General","{op o:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"),
("List",
- "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"),
+ "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, op @:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"),
("Relational",
"{count:forall 'a. 'a list -> int, max:forall 'a. 'a list -> 'a, min:forall 'a. 'a list -> 'a, sum:forall 'a. 'a list -> 'a}"),
("String",
@@ -824,5 +859,5 @@ val it =
("count","forall 'a. 'a list -> int"),
("env","unit -> (string * string) list"),("false","bool"),
("ignore","forall 'a. 'a -> unit"),("it","(string * string) list"),
- ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),
- ("max","forall 'a. 'a list -> 'a"),...] : (string * string) list
+ ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),...]
+ : (string * string) list
diff --git a/src/test/resources/script/simple.sml b/src/test/resources/script/simple.sml
index 8a23c918..0bb2e151 100644
--- a/src/test/resources/script/simple.sml
+++ b/src/test/resources/script/simple.sml
@@ -71,7 +71,7 @@ str "" 20 [];
[{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"}];
[{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"},{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"}];
-{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16};
+{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16};
(*
sml prints the following, but we cannot split types yet:
diff --git a/src/test/resources/script/simple.sml.out b/src/test/resources/script/simple.sml.out
index 68430ed4..934a215e 100644
--- a/src/test/resources/script/simple.sml.out
+++ b/src/test/resources/script/simple.sml.out
@@ -166,10 +166,10 @@ val it =
{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",
d="ddddddddd"}] : {a:string, b:string, c:string, d:string} list
-{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16};
+{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16};
val it =
- {a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16}
- : {a:int, b:int, c:int, d:int, e:int, f:int, g:int, h:int, i:int, j:int, k:int, l:int, m:int, n:int, o:int, p:int}
+ {a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16}
+ : {a:int, b:int, c:int, d:int, e:int, f:int, g:int, h:int, i:int, j:int, k:int, l:int, m:int, n:int, o_:int, p:int}
(*
sml prints the following, but we cannot split types yet: