From 07be232024b0d660d8a48fed354115bf190b92de Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Tue, 24 Aug 2021 09:38:51 -0600 Subject: [PATCH 01/12] Feature: Incremental sequences, sorting. --- dfx.json | 3 ++ examples/sort/Main.mo | 5 ++ examples/sort/Sort.mo | 5 ++ src/data/Meta.mo | 36 +++++++++++++ src/data/README.md | 13 +++++ src/data/Sequence.mo | 120 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 examples/sort/Main.mo create mode 100644 examples/sort/Sort.mo create mode 100644 src/data/Meta.mo create mode 100644 src/data/README.md create mode 100644 src/data/Sequence.mo diff --git a/dfx.json b/dfx.json index 61f7ee6..507d6f8 100644 --- a/dfx.json +++ b/dfx.json @@ -2,6 +2,9 @@ "canisters": { "Calc": { "main": "examples/calc/Main.mo" + }, + "Sort": { + "main": "examples/sort/Main.mo" } }, "defaults": { diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo new file mode 100644 index 0000000..9eee239 --- /dev/null +++ b/examples/sort/Main.mo @@ -0,0 +1,5 @@ +import Sort "Sort" + +actor { + // to do +} diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo new file mode 100644 index 0000000..9ca77f7 --- /dev/null +++ b/examples/sort/Sort.mo @@ -0,0 +1,5 @@ +import Seq "../../src/data/Sequence"; + +module { + // to do +} diff --git a/src/data/Meta.mo b/src/data/Meta.mo new file mode 100644 index 0000000..247e144 --- /dev/null +++ b/src/data/Meta.mo @@ -0,0 +1,36 @@ +import Engine "../Engine"; + +import H "mo:base/Hash"; +import L "mo:base/List"; +import R "mo:base/Result"; +import P "mo:base/Prelude"; +import Int "mo:base/Int"; +import Debug "mo:base/Debug"; +import Text "mo:base/Text"; + + +module { + + /// Names as untyped symbol trees. + /// Names serve as locally-unique dynamic identifiers. + public type Name = { + #none; + #int : Int; + #nat : Nat; + #text : Text; + #bin : (Name, Name); + #tri : (Name, Name, Name); + #cons : (Name, [ Name ]); + #record : [(Name, Name)]; + }; + + // Levels define [Cartesian trees](https://en.wikipedia.org/wiki/Cartesian_tree). + public type Level = Nat; + + // Meta data within inductive components of incremental data structures. + public type Meta = { + name : Name; + level : Level; + }; +} + diff --git a/src/data/README.md b/src/data/README.md new file mode 100644 index 0000000..673c2ca --- /dev/null +++ b/src/data/README.md @@ -0,0 +1,13 @@ +# Incremental data (structures) + +Following ideas from earlier [(nominal) Adapton implementations and papers](http://adapton.org). + +- [ ] Stacks -- as singly linked lists. +- [ ] Sequences -- as binary [Cartesian trees](https://en.wikipedia.org/wiki/Cartesian_tree). +- [ ] Maps -- as [binary tries](https://en.wikipedia.org/wiki/Trie). + +Each structure and its algorithms use names in its inductive definition. + +During change propagation, these names distinguish independent components +of each structure as it undergoes incremental change. + diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo new file mode 100644 index 0000000..181f4e3 --- /dev/null +++ b/src/data/Sequence.mo @@ -0,0 +1,120 @@ +import Engine "../Engine"; +import Meta "Meta"; + +import H "mo:base/Hash"; +import L "mo:base/List"; +import R "mo:base/Result"; +import P "mo:base/Prelude"; +import Int "mo:base/Int"; +import Debug "mo:base/Debug"; +import Text "mo:base/Text"; + + +module { + +public type Name = Meta.Name; + +public type ListMeta = Meta.Meta; + +public type TreeMeta = { + name : Name; + level : Meta.Level; + size : Nat +}; + +// Expresssions serve as "spreadsheet formula" for sequences. +public type Exp = { + // alloc reduces to #thunk case *before* evaluation + #alloc: (Name, Exp); + // thunk case permits fine-grained re-use / re-evaluation + #thunk: Name; + // Sequence literal definition/construction + #cons: (Exp, ListMeta, Exp); + #nil; + #val: Val_; + // Sequence operations + #toTree: Exp; + #toList: Exp; + #max: Exp; + //#sort: Exp; + //#median: Exp; + #err: Error_; +}; + +public type Val = { + // empty list; empty tree. + #nil; + // cons list: left value is list element; right is sub-list. + #cons: (Val, ListMeta, Val); + // binary tree: left/right values are sub-trees. + #bin: (Val, TreeMeta, Val); + #thunk: Name; + #val: Val_; +}; + +public type Error = { + #typeError; + #engineError; // to do -- improve with separate PR + #emptySequence // no max/min/median defined when sequence is empty +}; + +public type Ops = { + valMax : (Val_, Val_) -> R.Result; + getVal : Val_ -> ?Val; + putExp : Exp -> Exp_; + getExp : Exp_ -> ?Exp; + putError : Error -> Error_; + getError : Error_ -> ?Error; +}; + +public class Sequence( + engine: Engine.Engine, + ops: Ops +) { + + public func eval(e : Exp) : R.Result, Error> { + evalRec(alloc(e)) + }; + + func alloc(e : Exp) : Exp { + switch e { + case (#thunk(n)) #thunk((n)); + case (#alloc(n, e)) + switch (engine.putThunk(n, ops.putExp(alloc(e)))) { + case (#err(err)) { loop { assert false } }; + case (#ok(n)) { #thunk(n) }; + }; + case _ { // to do -- recursive cases + loop { assert false } + }; + } + }; + + func evalRec(exp : Exp) : R.Result, Error> { + switch exp { + case (#alloc(n, e)) loop { assert false }; + case (#thunk(n)) + switch (engine.get(n)) { + case (#err(_) or #ok(#err _)) #err(#engineError); + case (#ok(#ok(res))) switch (ops.getVal(res)) { + case null { #err(#typeError) }; + case (?v) { #ok(v) }; + }; + }; + case _ loop { assert false }; + // #cons: (Exp, ListMeta, Exp); + // #nil; + // #val: Val_; + // // Sequence operations + // #toTree: Exp; + // #toList: Exp; + // #max: Exp; + // //#sort: Exp; + // //#median: Exp; + // #err: Error_; + } + }; + +}; + +} From a0b817ebf065e9cf12cd415e30a4cc90a6a5b9b1 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Fri, 27 Aug 2021 12:41:16 -0600 Subject: [PATCH 02/12] plumbing and input data for test. --- examples/sort/Main.mo | 28 +++++++++++++- examples/sort/Sort.mo | 87 ++++++++++++++++++++++++++++++++++++++++++- src/data/Meta.mo | 18 +++++++-- src/data/Sequence.mo | 15 +++++--- 4 files changed, 137 insertions(+), 11 deletions(-) diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo index 9eee239..3e268c3 100644 --- a/examples/sort/Main.mo +++ b/examples/sort/Main.mo @@ -1,5 +1,29 @@ -import Sort "Sort" +import Meta "../../src/data/Meta"; +import Sort "Sort"; actor { - // to do + var sort = Sort.Sort(); + + var metaCounter : Nat = 0; + func metaAlloc () : Meta.Meta { + let level = Meta.Level.ofNat(metaCounter); + let name = #nat metaCounter; + let meta = {level; name}; + metaCounter += 1; + meta + }; + + public query func test() : async () { + let r = + sort.eval( + #seq(#toTree( + #array( + [(#val(#num 4), metaAlloc()), + (#val(#num 8), metaAlloc()), + (#val(#num 2), metaAlloc()), + (#val(#num 7), metaAlloc()), + (#val(#num 1), metaAlloc()), + (#val(#num 9), metaAlloc()), + (#val(#num 6), metaAlloc())])))); + }; } diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo index 9ca77f7..0bd8085 100644 --- a/examples/sort/Sort.mo +++ b/examples/sort/Sort.mo @@ -1,5 +1,90 @@ +import Engine "../../src/Engine"; import Seq "../../src/data/Sequence"; +import Meta "../../src/data/Meta"; +import R "mo:base/Result"; +import L "mo:base/List"; +import Text "mo:base/Text"; +/** + Incremental sorting example. + Uses adapton for representing stream thunks and caching them as data changes. + */ module { - // to do + +public type Name = Meta.Name; + +public type Val = { + #unit; + #num : Nat; + #seq : Seq.Val; +}; + +public type Error = { + #typeMismatch; + #seq : Seq.Error; +}; + +public type Exp = { + #unit; + #num : Nat; + #seq : Seq.Exp; +}; + +public class Sort() { + /* -- cache implementation, via adapton package -- */ + public var engine : Engine.Engine = + Engine.Engine( + { + nameEq=func (x:Name, y:Name) : Bool { x == y }; + valEq=func (x:Val, y:Val) : Bool { x == y }; + closureEq=func (x:Exp, y:Exp) : Bool { x == y }; + errorEq=func (x:Error, y:Error) : Bool { x == y }; + nameHash=Meta.Name.hash; + cyclicDependency=func (stack:L.List, name:Name) : Error { + assert false; loop { } + } + }, + true // logging + ); + public var engineIsInit = false; + + public var seq = Seq.Sequence( + engine, + { + valMax = func(v1 : Val, v2 : Val) : R.Result { + switch (v1, v2) { + case (#num n1, #num n2) + #ok(#num(if (n1 > n2) n1 else n2)); + case _ #err(#typeMismatch); + } + }; + getVal = func(v : Val) : ?Seq.Val = + switch v { case (#seq(s)) ?s; case _ null }; + putVal = func(v : Seq.Val) : Val = #seq(v); + putExp = func(e : Seq.Exp) : Exp = #seq(e); + getExp = func(e : Exp) : ?Seq.Exp = + switch e { case (#seq(e)) ?e; case _ null }; + putError = func(e : Seq.Error) : Error = #seq(e); + } + ); + + func evalRec(exp : Exp) : R.Result { + switch exp { + case (#unit) #ok(#unit); + case (#num n) #ok(#num n); + case (#seq e) seq.eval(e); + } + }; + + public func eval(exp : Exp) : R.Result { + if (not engineIsInit) { + engine.init({eval=evalRec}); + engineIsInit := true + }; + evalRec(exp) + }; + +}; + } + diff --git a/src/data/Meta.mo b/src/data/Meta.mo index 247e144..34a5b31 100644 --- a/src/data/Meta.mo +++ b/src/data/Meta.mo @@ -7,7 +7,8 @@ import P "mo:base/Prelude"; import Int "mo:base/Int"; import Debug "mo:base/Debug"; import Text "mo:base/Text"; - +import Nat "mo:base/Nat"; +import Nat32 "mo:base/Nat32"; module { @@ -23,7 +24,7 @@ module { #cons : (Name, [ Name ]); #record : [(Name, Name)]; }; - + // Levels define [Cartesian trees](https://en.wikipedia.org/wiki/Cartesian_tree). public type Level = Nat; @@ -32,5 +33,16 @@ module { name : Name; level : Level; }; + + public module Level { + public func ofNat(n : Nat) : Level { + Nat32.toNat(Nat32.bitcountLeadingZero(H.hash(n))) + } + }; + + public module Name { + public func hash (n : Name) : H.Hash { + loop { assert false } + }; + }; } - diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 181f4e3..2e6d2b6 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -24,6 +24,8 @@ public type TreeMeta = { // Expresssions serve as "spreadsheet formula" for sequences. public type Exp = { + // arrays for small test inputs, and little else. + #array: [ (Exp, Meta.Meta) ]; // alloc reduces to #thunk case *before* evaluation #alloc: (Name, Exp); // thunk case permits fine-grained re-use / re-evaluation @@ -63,17 +65,20 @@ public type Ops = { getVal : Val_ -> ?Val; putExp : Exp -> Exp_; getExp : Exp_ -> ?Exp; + putVal : Val -> Val_; putError : Error -> Error_; - getError : Error_ -> ?Error; }; -public class Sequence( +public class Sequence( engine: Engine.Engine, ops: Ops ) { - public func eval(e : Exp) : R.Result, Error> { - evalRec(alloc(e)) + public func eval(e : Exp) : R.Result { + switch (evalRec(alloc(e))) { + case (#ok(v)) #ok(ops.putVal(v)); + case (#err(e)) #err(ops.putError(e)); + } }; func alloc(e : Exp) : Exp { @@ -95,7 +100,7 @@ public class Sequence( case (#alloc(n, e)) loop { assert false }; case (#thunk(n)) switch (engine.get(n)) { - case (#err(_) or #ok(#err _)) #err(#engineError); + case (#err(_) or #ok(#err _)) #err(#engineError); case (#ok(#ok(res))) switch (ops.getVal(res)) { case null { #err(#typeError) }; case (?v) { #ok(v) }; From ca75e3c0244c09b9d423f121faad821da609e3db Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Sat, 28 Aug 2021 10:57:03 -0600 Subject: [PATCH 03/12] refactor common code --- examples/sort/Main.mo | 25 ++++++++----------------- src/data/Meta.mo | 12 ++++++++++++ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo index 3e268c3..2cda25f 100644 --- a/examples/sort/Main.mo +++ b/examples/sort/Main.mo @@ -3,27 +3,18 @@ import Sort "Sort"; actor { var sort = Sort.Sort(); - - var metaCounter : Nat = 0; - func metaAlloc () : Meta.Meta { - let level = Meta.Level.ofNat(metaCounter); - let name = #nat metaCounter; - let meta = {level; name}; - metaCounter += 1; - meta - }; - + let meta = Meta.Counter(); public query func test() : async () { let r = sort.eval( #seq(#toTree( #array( - [(#val(#num 4), metaAlloc()), - (#val(#num 8), metaAlloc()), - (#val(#num 2), metaAlloc()), - (#val(#num 7), metaAlloc()), - (#val(#num 1), metaAlloc()), - (#val(#num 9), metaAlloc()), - (#val(#num 6), metaAlloc())])))); + [(#val(#num 4), meta.next()), + (#val(#num 8), meta.next()), + (#val(#num 2), meta.next()), + (#val(#num 7), meta.next()), + (#val(#num 1), meta.next()), + (#val(#num 9), meta.next()), + (#val(#num 6), meta.next())])))); }; } diff --git a/src/data/Meta.mo b/src/data/Meta.mo index 34a5b31..70c5bbf 100644 --- a/src/data/Meta.mo +++ b/src/data/Meta.mo @@ -45,4 +45,16 @@ module { loop { assert false } }; }; + + public class Counter() { + var counter : Nat = 0; + public func next () : Meta { + let level = Level.ofNat(counter); + let name = #nat counter; + let meta = {level; name}; + counter += 1; + meta + }; + }; + } From 58553b87b384f33904d14f0554cd207c083ab7b1 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Sun, 5 Sep 2021 05:41:56 -0600 Subject: [PATCH 04/12] nits: larger test input; remove extra type arg. --- examples/sort/Main.mo | 26 +++++++++++++++++++------- examples/sort/Sort.mo | 2 +- src/data/Sequence.mo | 25 ++++++++++++------------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo index 2cda25f..40e929c 100644 --- a/examples/sort/Main.mo +++ b/examples/sort/Main.mo @@ -9,12 +9,24 @@ actor { sort.eval( #seq(#toTree( #array( - [(#val(#num 4), meta.next()), - (#val(#num 8), meta.next()), - (#val(#num 2), meta.next()), - (#val(#num 7), meta.next()), - (#val(#num 1), meta.next()), - (#val(#num 9), meta.next()), - (#val(#num 6), meta.next())])))); + [ + (#val(#num 04), meta.next()), + (#val(#num 16), meta.next()), + (#val(#num 02), meta.next()), + (#val(#num 07), meta.next()), + (#val(#num 11), meta.next()), + (#val(#num 09), meta.next()), + (#val(#num 06), meta.next()), + + (#val(#num 23), meta.next()), + (#val(#num 08), meta.next()), + (#val(#num 13), meta.next()), + (#val(#num 06), meta.next()), + (#val(#num 14), meta.next()), + (#val(#num 17), meta.next()), + (#val(#num 16), meta.next()), + ] + )) + )); }; } diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo index 0bd8085..4b136a7 100644 --- a/examples/sort/Sort.mo +++ b/examples/sort/Sort.mo @@ -27,7 +27,7 @@ public type Error = { public type Exp = { #unit; #num : Nat; - #seq : Seq.Exp; + #seq : Seq.Exp; }; public class Sort() { diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 2e6d2b6..0e47c63 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -23,24 +23,23 @@ public type TreeMeta = { }; // Expresssions serve as "spreadsheet formula" for sequences. -public type Exp = { +public type Exp = { // arrays for small test inputs, and little else. - #array: [ (Exp, Meta.Meta) ]; + #array: [ (Exp, Meta.Meta) ]; // alloc reduces to #thunk case *before* evaluation - #alloc: (Name, Exp); + #alloc: (Name, Exp); // thunk case permits fine-grained re-use / re-evaluation #thunk: Name; // Sequence literal definition/construction - #cons: (Exp, ListMeta, Exp); + #cons: (Exp, ListMeta, Exp); #nil; #val: Val_; // Sequence operations - #toTree: Exp; - #toList: Exp; - #max: Exp; + #toTree: Exp; + #toList: Exp; + #max: Exp; //#sort: Exp; //#median: Exp; - #err: Error_; }; public type Val = { @@ -63,8 +62,8 @@ public type Error = { public type Ops = { valMax : (Val_, Val_) -> R.Result; getVal : Val_ -> ?Val; - putExp : Exp -> Exp_; - getExp : Exp_ -> ?Exp; + putExp : Exp -> Exp_; + getExp : Exp_ -> ?Exp; putVal : Val -> Val_; putError : Error -> Error_; }; @@ -74,14 +73,14 @@ public class Sequence( ops: Ops ) { - public func eval(e : Exp) : R.Result { + public func eval(e : Exp) : R.Result { switch (evalRec(alloc(e))) { case (#ok(v)) #ok(ops.putVal(v)); case (#err(e)) #err(ops.putError(e)); } }; - func alloc(e : Exp) : Exp { + func alloc(e : Exp) : Exp { switch e { case (#thunk(n)) #thunk((n)); case (#alloc(n, e)) @@ -95,7 +94,7 @@ public class Sequence( } }; - func evalRec(exp : Exp) : R.Result, Error> { + func evalRec(exp : Exp) : R.Result, Error> { switch exp { case (#alloc(n, e)) loop { assert false }; case (#thunk(n)) From c0a36ddef0c75a64b9096192d6f73ad1ea09b8d6 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Tue, 7 Sep 2021 14:04:27 -0600 Subject: [PATCH 05/12] re-adjust example; start eval plumbing. --- examples/sort/Main.mo | 5 +- examples/sort/Sort.mo | 8 +-- src/data/Sequence.mo | 114 +++++++++++++++++++++++++++++++----------- 3 files changed, 92 insertions(+), 35 deletions(-) diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo index 40e929c..b6ac053 100644 --- a/examples/sort/Main.mo +++ b/examples/sort/Main.mo @@ -7,7 +7,7 @@ actor { public query func test() : async () { let r = sort.eval( - #seq(#toTree( + #seq(#treeOfStream(#streamOfArray( #array( [ (#val(#num 04), meta.next()), @@ -26,7 +26,6 @@ actor { (#val(#num 17), meta.next()), (#val(#num 16), meta.next()), ] - )) - )); + ))))); }; } diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo index 4b136a7..9925f38 100644 --- a/examples/sort/Sort.mo +++ b/examples/sort/Sort.mo @@ -21,7 +21,7 @@ public type Val = { public type Error = { #typeMismatch; - #seq : Seq.Error; + #seq : Seq.Error; }; public type Exp = { @@ -61,10 +61,10 @@ public class Sort() { getVal = func(v : Val) : ?Seq.Val = switch v { case (#seq(s)) ?s; case _ null }; putVal = func(v : Seq.Val) : Val = #seq(v); - putExp = func(e : Seq.Exp) : Exp = #seq(e); - getExp = func(e : Exp) : ?Seq.Exp = + putExp = func(e : Seq.Exp) : Exp = #seq(e); + getExp = func(e : Exp) : ?Seq.Exp = switch e { case (#seq(e)) ?e; case _ null }; - putError = func(e : Seq.Error) : Error = #seq(e); + putError = func(e : Seq.Error) : Error = #seq(e); } ); diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 0e47c63..a9956fb 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -8,13 +8,13 @@ import P "mo:base/Prelude"; import Int "mo:base/Int"; import Debug "mo:base/Debug"; import Text "mo:base/Text"; - +import Buffer "mo:base/Buffer"; module { public type Name = Meta.Name; -public type ListMeta = Meta.Meta; +public type Meta = Meta.Meta; public type TreeMeta = { name : Name; @@ -26,37 +26,58 @@ public type TreeMeta = { public type Exp = { // arrays for small test inputs, and little else. #array: [ (Exp, Meta.Meta) ]; - // alloc reduces to #thunk case *before* evaluation - #alloc: (Name, Exp); - // thunk case permits fine-grained re-use / re-evaluation - #thunk: Name; - // Sequence literal definition/construction - #cons: (Exp, ListMeta, Exp); + // each `#put` case transforms into an #at case *before any evaluation*. + #put: (Name, Exp); + // `#at` case permits fine-grained re-use / re-evaluation via Adapton names. + #at: Name; + // Stream-literal definition/construction + #cons: (Exp, Meta, Exp); #nil; #val: Val_; // Sequence operations - #toTree: Exp; - #toList: Exp; - #max: Exp; + #streamOfArray: Exp; + #treeOfStream: Exp; + #maxOfTree: Exp; //#sort: Exp; //#median: Exp; }; public type Val = { + // arrays for small test inputs, and little else. + #array: [ (Val, Meta.Meta) ]; // empty list; empty tree. #nil; - // cons list: left value is list element; right is sub-list. - #cons: (Val, ListMeta, Val); + // lazy list / stream cell: left value is stream "now"; right is stream "later". + #cons: (Val, Meta, Val); // binary tree: left/right values are sub-trees. #bin: (Val, TreeMeta, Val); - #thunk: Name; + #at: Name; #val: Val_; }; -public type Error = { - #typeError; - #engineError; // to do -- improve with separate PR - #emptySequence // no max/min/median defined when sequence is empty +/// Each sequence representation has a different run-time type, +/// with associated checks for its operations. +public type SeqType = { + #array; + #stream; + #tree; +}; + +/// Result type for all "meta level" operations returning an `X`. +public type Result = R.Result>; + +/// Evaluation results in a `Val` on success. +public type EvalResult = Result, Val_>; + +public type Error = { + /// Wrong value form: Not from this language module. + #notOurVal : Val_; + /// Wrong value form: Type mismatch. + #doNotHave : (SeqType, Val); + // no max/min/median defined when sequence is empty + #emptySequence; + // to do -- improve with separate PR + #engineError; }; public type Ops = { @@ -65,7 +86,7 @@ public type Ops = { putExp : Exp -> Exp_; getExp : Exp_ -> ?Exp; putVal : Val -> Val_; - putError : Error -> Error_; + putError : Error -> Error_; }; public class Sequence( @@ -82,11 +103,11 @@ public class Sequence( func alloc(e : Exp) : Exp { switch e { - case (#thunk(n)) #thunk((n)); - case (#alloc(n, e)) + case (#at(n)) #at((n)); + case (#put(n, e)) switch (engine.putThunk(n, ops.putExp(alloc(e)))) { case (#err(err)) { loop { assert false } }; - case (#ok(n)) { #thunk(n) }; + case (#ok(n)) { #at(n) }; }; case _ { // to do -- recursive cases loop { assert false } @@ -94,23 +115,60 @@ public class Sequence( } }; - func evalRec(exp : Exp) : R.Result, Error> { + public func haveArray(arr : Val) : Result<[(Val, Meta)], Val_> { + switch arr { + case (#array(a)) { #ok(a) }; + case _ { #err(#doNotHave(#array, arr)) }; + }; + }; + + public func streamOfArray(input : Val) : EvalResult { + let array = haveArray(input); + loop { assert false }; + }; + + public func treeOfStream(iter : Val) : EvalResult { + loop { assert false } + }; + + func evalRec(exp : Exp) : EvalResult { switch exp { - case (#alloc(n, e)) loop { assert false }; - case (#thunk(n)) + case (#put(n, e)) loop { assert false }; + case (#array(arr)) { + let vals = Buffer.Buffer<(Val, Meta)>(arr.size()); + for ((e, meta) in arr.vals()) { + switch (evalRec(e)) { + case (#ok(v)) { vals.add((v, meta)) }; + case (#err(e)) { return #err(e) }; + }; + }; + #ok(#array(vals.toArray())) + }; + case (#at(n)) switch (engine.get(n)) { case (#err(_) or #ok(#err _)) #err(#engineError); case (#ok(#ok(res))) switch (ops.getVal(res)) { - case null { #err(#typeError) }; + case null { #err(#notOurVal(res)) }; case (?v) { #ok(v) }; }; }; - case _ loop { assert false }; + case (#streamOfArray(a)) { + switch (evalRec(a)) { + case (#err(err)) { #err(err) }; + case (#ok(array)) { streamOfArray(array) }; + } + }; + case (#treeOfStream(e)) { + switch (evalRec(e)) { + case (#err(err)) { #err(err) }; + case (#ok(list)) { treeOfStream(list) }; + } + }; + case _ { loop { assert false } }; // #cons: (Exp, ListMeta, Exp); // #nil; // #val: Val_; // // Sequence operations - // #toTree: Exp; // #toList: Exp; // #max: Exp; // //#sort: Exp; From 8ff0b26c5c9f2d84530456035b7e97b94c8bd450 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Tue, 7 Sep 2021 20:52:09 -0600 Subject: [PATCH 06/12] more plumbing --- examples/sort/Sort.mo | 2 + src/data/Sequence.mo | 169 ++++++++++++++++++++++++++++++++---------- 2 files changed, 130 insertions(+), 41 deletions(-) diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo index 9925f38..adbcdcc 100644 --- a/examples/sort/Sort.mo +++ b/examples/sort/Sort.mo @@ -65,6 +65,8 @@ public class Sort() { getExp = func(e : Exp) : ?Seq.Exp = switch e { case (#seq(e)) ?e; case _ null }; putError = func(e : Seq.Error) : Error = #seq(e); + getError = func(e : Error) : ?Seq.Error = + switch e { case (#seq(e)) ?e; case _ null }; } ); diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index a9956fb..2f07d87 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -37,22 +37,54 @@ public type Exp = { // Sequence operations #streamOfArray: Exp; #treeOfStream: Exp; + #treeOfStreamRec: { parentLevel : ?Nat; stream : Stream; subTree : Tree }; #maxOfTree: Exp; //#sort: Exp; //#median: Exp; }; +public type Array = [ (Val, Meta.Meta) ]; + +public type ArrayStream = { + array : Array; + offset : Nat; +}; + +public type Cons = (Val, Meta, Val); + +public type Bin = (Val, TreeMeta, Val); + public type Val = { + // value allocated at a name, stored by an adapton thunk or ref. + #at: Name; // arrays for small test inputs, and little else. - #array: [ (Val, Meta.Meta) ]; + #array: Array; + // array streams: Special stream where source is a fixed array. + #arrayStream: ArrayStream; // empty list; empty tree. #nil; // lazy list / stream cell: left value is stream "now"; right is stream "later". - #cons: (Val, Meta, Val); - // binary tree: left/right values are sub-trees. - #bin: (Val, TreeMeta, Val); - #at: Name; - #val: Val_; + #cons: Cons; + // binary tree: binary case. + #bin: Bin; + // binary tree: leaf case. + #leaf: Val_; +}; + +/// Cartesian trees as balanced representations for sequences. +public type Tree = { + #nil; + #bin: Bin; + #leaf: Val_; +}; + +public type Stream = { + // empty list; empty tree. + #nil; + // lazy list / stream cell: left value is stream "now"; right is stream "later". + #cons: Cons; + // array streams: Special stream where source is a fixed array. + #arrayStream: ArrayStream; }; /// Each sequence representation has a different run-time type, @@ -87,6 +119,7 @@ public type Ops = { getExp : Exp_ -> ?Exp; putVal : Val -> Val_; putError : Error -> Error_; + getError : Error_ -> ?Error; }; public class Sequence( @@ -94,6 +127,7 @@ public class Sequence( ops: Ops ) { + /// Evaluate expression into a result. public func eval(e : Exp) : R.Result { switch (evalRec(alloc(e))) { case (#ok(v)) #ok(ops.putVal(v)); @@ -115,35 +149,94 @@ public class Sequence( } }; - public func haveArray(arr : Val) : Result<[(Val, Meta)], Val_> { - switch arr { + /// Check canonical array forms. + public func haveArray(v : Val) : Result<[(Val, Meta)], Val_> { + switch v { case (#array(a)) { #ok(a) }; - case _ { #err(#doNotHave(#array, arr)) }; + case _ { #err(#doNotHave(#array, v)) }; }; }; - public func streamOfArray(input : Val) : EvalResult { - let array = haveArray(input); - loop { assert false }; + /// Check canonical stream forms. + public func haveStream(v : Val) : Result, Val_> { + switch v { + case (#arrayStream(s)) { #ok(#arrayStream(s)) }; + case (#cons(c)) { #ok(#cons(c)) }; + case (#nil) { #ok(#nil) }; + case _ { #err(#doNotHave(#stream, v)) }; + } + }; + + /// Transforms an array into a stream. + public func streamOfArray(v : Val) : EvalResult { + switch(haveArray(v)) { + case (#ok(array)) { #ok(#arrayStream({array; offset = 0})) }; + case (#err(err)) { #err(err) }; + } }; - public func treeOfStream(iter : Val) : EvalResult { + func call(name : Name, exp : Exp) : EvalResult { + let thunk = + engine.putThunk( + name, ops.putExp(exp) + ); + switch thunk { + case (#ok(v)) { + switch(engine.get(v)) { + case (#err(err)) { #err(#engineError) }; + case (#ok(v)) { + switch(v) { + case (#ok(v)) { + switch (ops.getVal(v)) { + case null { #err(#engineError) }; + case (?v) { #ok(v) }; + } + }; + case (#err(err)) { + switch (ops.getError(err)) { + case null { #err(#engineError) }; + case (?e) { #err(e) }; + } + }; + } + }; + } + }; + case (#err(err)) { #err(#engineError) }; + } + }; + + /// Transforms a stream into a tree. + public func treeOfStreamRec(parentLevel : ?Nat, s : Stream, tree : Tree) : EvalResult { + let t = + call( + #text("test"), + #treeOfStreamRec({ parentLevel; stream = s; subTree = tree }) + ); loop { assert false } }; + /// Transforms a stream into a tree. + public func treeOfStream(s : Val) : EvalResult { + switch(haveStream(s)) { + case (#ok(s)) { treeOfStreamRec(null, s, #nil) }; + case (#err(err)) { #err(err) }; + }; + }; + func evalRec(exp : Exp) : EvalResult { switch exp { case (#put(n, e)) loop { assert false }; case (#array(arr)) { - let vals = Buffer.Buffer<(Val, Meta)>(arr.size()); - for ((e, meta) in arr.vals()) { - switch (evalRec(e)) { - case (#ok(v)) { vals.add((v, meta)) }; - case (#err(e)) { return #err(e) }; - }; - }; - #ok(#array(vals.toArray())) - }; + let vals = Buffer.Buffer<(Val, Meta)>(arr.size()); + for ((e, meta) in arr.vals()) { + switch (evalRec(e)) { + case (#ok(v)) { vals.add((v, meta)) }; + case (#err(e)) { return #err(e) }; + }; + }; + #ok(#array(vals.toArray())) + }; case (#at(n)) switch (engine.get(n)) { case (#err(_) or #ok(#err _)) #err(#engineError); @@ -153,27 +246,21 @@ public class Sequence( }; }; case (#streamOfArray(a)) { - switch (evalRec(a)) { - case (#err(err)) { #err(err) }; - case (#ok(array)) { streamOfArray(array) }; - } - }; + switch (evalRec(a)) { + case (#err(err)) { #err(err) }; + case (#ok(array)) { streamOfArray(array) }; + } + }; + case (#treeOfStreamRec(args)) { + treeOfStreamRec(args.parentLevel, args.stream, args.subTree) + }; case (#treeOfStream(e)) { - switch (evalRec(e)) { - case (#err(err)) { #err(err) }; - case (#ok(list)) { treeOfStream(list) }; - } - }; + switch (evalRec(e)) { + case (#err(err)) { #err(err) }; + case (#ok(list)) { treeOfStream(list) }; + } + }; case _ { loop { assert false } }; - // #cons: (Exp, ListMeta, Exp); - // #nil; - // #val: Val_; - // // Sequence operations - // #toList: Exp; - // #max: Exp; - // //#sort: Exp; - // //#median: Exp; - // #err: Error_; } }; From 6fafb3ea7fdc422bb4b6dcce33a4cc74eafbc3ba Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 10:23:11 -0600 Subject: [PATCH 07/12] Build (binary) cartesian trees from (linear) streams. --- src/Engine.mo | 6 +++ src/data/Sequence.mo | 107 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/src/Engine.mo b/src/Engine.mo index 126a71d..8cd677a 100644 --- a/src/Engine.mo +++ b/src/Engine.mo @@ -62,6 +62,12 @@ module { context.logOps.end(tag) }; + public func nest(name : Name, body : () -> X) : X { + // to do -- enter nested namespace using name + body() + // to do -- resume original namespace + }; + public func put(name:Name, val:Val) : R.Result { logBegin(); let newRefNode : G.Ref = { diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 2f07d87..904dea7 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -31,7 +31,7 @@ public type Exp = { // `#at` case permits fine-grained re-use / re-evaluation via Adapton names. #at: Name; // Stream-literal definition/construction - #cons: (Exp, Meta, Exp); + #cons: { head: Exp; meta: Exp; tail: Exp }; #nil; #val: Val_; // Sequence operations @@ -52,7 +52,11 @@ public type ArrayStream = { public type Cons = (Val, Meta, Val); -public type Bin = (Val, TreeMeta, Val); +public type Bin = { + left : Val; + meta : TreeMeta; + right : Val; +}; public type Val = { // value allocated at a name, stored by an adapton thunk or ref. @@ -167,6 +171,14 @@ public class Sequence( } }; + /// Check canonical stream forms. + public func haveTree(v : Val) : Result, Val_> { + switch v { + // to do + case _ { #err(#doNotHave(#tree, v)) }; + } + }; + /// Transforms an array into a stream. public func streamOfArray(v : Val) : EvalResult { switch(haveArray(v)) { @@ -175,7 +187,7 @@ public class Sequence( } }; - func call(name : Name, exp : Exp) : EvalResult { + func memoCall(name : Name, exp : Exp) : EvalResult { let thunk = engine.putThunk( name, ops.putExp(exp) @@ -206,22 +218,95 @@ public class Sequence( } }; + /// number of elms; ignore internal nodes + public func treeSize (t : Tree) : Nat { + assert false; loop {} + }; + + public func treeLevel (t : Tree) : Nat { + assert false; loop {} + }; + + public func streamNext (s : Stream) : ?Cons { + assert false; loop {} + }; + + public func streamMeta (s : Stream) : EvalResult { + assert false; loop {} + }; + + public func streamTail (s : Stream) : EvalResult { + assert false; loop {} + }; + + public func resultPairSplit(r : EvalResult) : Result<(Val, Val), Val_> { + assert false; loop {} + }; + + public func resultPair(v1 : Val, v2 : Val) : EvalResult { + assert false; loop {} + }; + /// Transforms a stream into a tree. - public func treeOfStreamRec(parentLevel : ?Nat, s : Stream, tree : Tree) : EvalResult { - let t = - call( - #text("test"), - #treeOfStreamRec({ parentLevel; stream = s; subTree = tree }) - ); - loop { assert false } + public func treeOfStreamRec(parentLevel : ?Nat, s : Stream, tree : Tree) + : EvalResult // (Tree, Stream), for the result and remaining stream. + { + switch (streamNext(s)) { + case null (resultPair(tree, #nil)); + case (?cons) { + let (head, meta, tail) = cons; + switch parentLevel { + case (?pl) { + if (meta.level > pl) { + return resultPair(tree, s) + } }; + case _ { }; + }; + if (meta.level < treeLevel(tree)) { + return resultPair(tree, s) + }; + let tailAsStream = switch (haveStream(s)) { + case (#ok(s)) s; + case (#err(e)) { return #err(e) } + }; + let (tree2, s2) = + switch (resultPairSplit(memoCall( + #bin(meta.name, #text("right")), + #treeOfStreamRec({ + parentLevel = ?meta.level; + stream = tailAsStream; + subTree = #leaf(ops.putVal(head)); + })))) { + case (#err(e)) { return #err(e) }; + case (#ok(v1, v2)) (v1, v2); + }; + let (tree2_, s2_) = switch (haveTree(tree2), haveStream(s2)) { + case (#ok(t), #ok(s)) (t, s); + case (#err(e1), _) { return #err(e1) }; + case (_, #err(e2)) { return #err(e2) }; + }; + let size = treeSize(tree) + treeSize(tree2_); + let tree3 = #bin({ left = tree; + meta = { level = meta.level; name = meta.name; size}; + right = tree2_ }); + memoCall( + #bin(meta.name, #text("root")), + #treeOfStreamRec({ + parentLevel; + stream = s2_; + subTree = tree3; + })); + } + } }; /// Transforms a stream into a tree. public func treeOfStream(s : Val) : EvalResult { + engine.nest(#text("treeOfStream"), func () : EvalResult { switch(haveStream(s)) { case (#ok(s)) { treeOfStreamRec(null, s, #nil) }; case (#err(err)) { #err(err) }; - }; + }}); }; func evalRec(exp : Exp) : EvalResult { From 6e6537d65164f66b0c31f8062dfda525ed09cc3d Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 11:31:56 -0600 Subject: [PATCH 08/12] fill holes from last commit. --- src/data/Sequence.mo | 46 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 904dea7..153e05b 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -23,6 +23,7 @@ public type TreeMeta = { }; // Expresssions serve as "spreadsheet formula" for sequences. +// Read "Val_" as "Any", but for our DSL system, not for Motoko. public type Exp = { // arrays for small test inputs, and little else. #array: [ (Exp, Meta.Meta) ]; @@ -71,8 +72,10 @@ public type Val = { #cons: Cons; // binary tree: binary case. #bin: Bin; - // binary tree: leaf case. + // binary tree: leaf case. *Any* value, from any language module. #leaf: Val_; + // pair of our values. + #pair: (Val, Val); }; /// Cartesian trees as balanced representations for sequences. @@ -97,6 +100,7 @@ public type SeqType = { #array; #stream; #tree; + #pair; }; /// Result type for all "meta level" operations returning an `X`. @@ -220,31 +224,45 @@ public class Sequence( /// number of elms; ignore internal nodes public func treeSize (t : Tree) : Nat { - assert false; loop {} + switch t { + case (#nil) 0; + case (#bin(b)) b.meta.size; + case (#leaf _) 1; + } }; public func treeLevel (t : Tree) : Nat { - assert false; loop {} + switch t { + case (#nil) 0; + case (#bin(b)) b.meta.level; + case (#leaf _) 0; + } }; public func streamNext (s : Stream) : ?Cons { - assert false; loop {} - }; - - public func streamMeta (s : Stream) : EvalResult { - assert false; loop {} - }; - - public func streamTail (s : Stream) : EvalResult { - assert false; loop {} + switch s { + case (#nil) null; + case (#arrayStream(a)) { + if(a.offset < a.array.size()) { + let (elm, meta) = a.array[a.offset]; + ?(elm, meta, #arrayStream{ offset = a.offset + 1; + array = a.array }) + } else null; + }; + case (#cons(c)) { ?c } + } }; public func resultPairSplit(r : EvalResult) : Result<(Val, Val), Val_> { - assert false; loop {} + switch r { + case (#ok(#pair(v1, v2))) { #ok((v1, v2)) }; + case (#ok(v)) { #err(#doNotHave(#pair, v)) }; + case (#err(e)) { #err(e) }; + } }; public func resultPair(v1 : Val, v2 : Val) : EvalResult { - assert false; loop {} + #ok(#pair(v1, v2)) }; /// Transforms a stream into a tree. From 642e2fcbe47fc5cbd1566cba116230b588280e1b Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 14:39:49 -0600 Subject: [PATCH 09/12] finish alloc cases. --- src/data/Sequence.mo | 65 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 153e05b..14c83ec 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -83,6 +83,8 @@ public type Tree = { #nil; #bin: Bin; #leaf: Val_; + // value allocated at a name, stored by an adapton thunk or ref. + #at: Name; }; public type Stream = { @@ -145,14 +147,27 @@ public class Sequence( func alloc(e : Exp) : Exp { switch e { + case (#array(a)) { + let elms = Buffer.Buffer<(Exp, Meta)>(a.size()); + for ((e, meta) in a.vals()) { + elms.add((alloc e, meta)) + }; + #array(elms.toArray()) + }; case (#at(n)) #at((n)); - case (#put(n, e)) + case (#put(n, e)) { switch (engine.putThunk(n, ops.putExp(alloc(e)))) { case (#err(err)) { loop { assert false } }; case (#ok(n)) { #at(n) }; - }; - case _ { // to do -- recursive cases - loop { assert false } + }}; + case (#val(v)) #val(v); + case (#streamOfArray(e)) #streamOfArray(alloc e); + case (#treeOfStream(e)) #treeOfStream(alloc e); + case (#treeOfStreamRec(e)) { assert false; loop {}}; + case (#maxOfTree(e)) #maxOfTree(alloc e); + case (#nil) #nil; + case (#cons(c)) { + #cons({head=alloc(c.head); meta=alloc(c.meta); tail=alloc(c.tail)}) }; } }; @@ -165,7 +180,7 @@ public class Sequence( }; }; - /// Check canonical stream forms. + /// Check canonical stream head form. public func haveStream(v : Val) : Result, Val_> { switch v { case (#arrayStream(s)) { #ok(#arrayStream(s)) }; @@ -175,10 +190,13 @@ public class Sequence( } }; - /// Check canonical stream forms. + /// Check canonical tree head form. public func haveTree(v : Val) : Result, Val_> { switch v { - // to do + case (#at n) { #ok(#at(n)) }; + case (#nil) { #ok(#nil) }; + case (#bin(b)) { #ok(#bin(b)) }; + case (#leaf v) { #ok(#leaf(v)) }; case _ { #err(#doNotHave(#tree, v)) }; } }; @@ -191,21 +209,22 @@ public class Sequence( } }; - func memoCall(name : Name, exp : Exp) : EvalResult { + // Returns the thunk, and its (eagerly-computed) value, as a pair. + func getPutThunk(name : Name, exp : Exp) : EvalResult { let thunk = engine.putThunk( name, ops.putExp(exp) ); switch thunk { - case (#ok(v)) { - switch(engine.get(v)) { + case (#ok(putName)) { + switch(engine.get(putName)) { case (#err(err)) { #err(#engineError) }; case (#ok(v)) { switch(v) { case (#ok(v)) { switch (ops.getVal(v)) { case null { #err(#engineError) }; - case (?v) { #ok(v) }; + case (?gotValue) { resultPair(#at(putName), gotValue) }; } }; case (#err(err)) { @@ -222,9 +241,28 @@ public class Sequence( } }; + // Does getPutThunk, but only returns the result of the thunk. + func memo(name : Name, exp : Exp) : EvalResult { + switch (getPutThunk(name, exp)) { + case (#err(e)) #err(e); + case (#ok(#pair((_, v)))) #ok(v); + case (#ok(_)) { assert false; loop {}}; + } + }; + + // Does getPutThunk, but returns the thunk and value as a Motoko pair. + func memo_(name : Name, exp : Exp) : Result<(Name, Val), Val_> { + switch (getPutThunk(name, exp)) { + case (#err(e)) #err(e); + case (#ok(#pair(#at(n), v))) #ok((n, v)); + case (#ok(_)) { assert false; loop {}}; + } + }; + /// number of elms; ignore internal nodes public func treeSize (t : Tree) : Nat { switch t { + case (#at(n)) { assert false; loop {}}; // query engine here? case (#nil) 0; case (#bin(b)) b.meta.size; case (#leaf _) 1; @@ -233,6 +271,7 @@ public class Sequence( public func treeLevel (t : Tree) : Nat { switch t { + case (#at(n)) { assert false; loop {}}; // to do -- query engine here? case (#nil) 0; case (#bin(b)) b.meta.level; case (#leaf _) 0; @@ -288,7 +327,7 @@ public class Sequence( case (#err(e)) { return #err(e) } }; let (tree2, s2) = - switch (resultPairSplit(memoCall( + switch (resultPairSplit(memo( #bin(meta.name, #text("right")), #treeOfStreamRec({ parentLevel = ?meta.level; @@ -307,7 +346,7 @@ public class Sequence( let tree3 = #bin({ left = tree; meta = { level = meta.level; name = meta.name; size}; right = tree2_ }); - memoCall( + memo( #bin(meta.name, #text("root")), #treeOfStreamRec({ parentLevel; From 43ffbebff743098f5ad303f47d259431845f63a4 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 14:56:03 -0600 Subject: [PATCH 10/12] invoke new code in CI. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 998e3c8..9b99345 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: dfx start --background dfx deploy --no-wallet dfx canister call Calc test + dfx canister call Sort test - name: Build docs run: /home/runner/.cache/dfinity/versions/$DFX_VERSION/mo-doc - name: Upload docs From ed3ba9472ae8ad5e7c40444909371c275340b16b Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 15:08:44 -0600 Subject: [PATCH 11/12] fixes for test --- src/data/Meta.mo | 6 +++++- src/data/Sequence.mo | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/data/Meta.mo b/src/data/Meta.mo index 70c5bbf..97a3c44 100644 --- a/src/data/Meta.mo +++ b/src/data/Meta.mo @@ -42,7 +42,11 @@ module { public module Name { public func hash (n : Name) : H.Hash { - loop { assert false } + switch n { + case (#none) Text.hash "none"; + case (#text(t)) Text.hash t; + case _ Text.hash "?"; // to do + } }; }; diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index 14c83ec..ab4b43d 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -38,12 +38,18 @@ public type Exp = { // Sequence operations #streamOfArray: Exp; #treeOfStream: Exp; - #treeOfStreamRec: { parentLevel : ?Nat; stream : Stream; subTree : Tree }; + #treeOfStreamRec: TreeOfStreamRec; #maxOfTree: Exp; //#sort: Exp; //#median: Exp; }; +public type TreeOfStreamRec = { + parentLevel : ?Nat; + stream : Stream; + subTree : Tree +}; + public type Array = [ (Val, Meta.Meta) ]; public type ArrayStream = { @@ -163,12 +169,12 @@ public class Sequence( case (#val(v)) #val(v); case (#streamOfArray(e)) #streamOfArray(alloc e); case (#treeOfStream(e)) #treeOfStream(alloc e); - case (#treeOfStreamRec(e)) { assert false; loop {}}; case (#maxOfTree(e)) #maxOfTree(alloc e); case (#nil) #nil; case (#cons(c)) { #cons({head=alloc(c.head); meta=alloc(c.meta); tail=alloc(c.tail)}) }; + case (#treeOfStreamRec(args)) { #treeOfStreamRec(args) }; } }; @@ -368,6 +374,7 @@ public class Sequence( func evalRec(exp : Exp) : EvalResult { switch exp { + case (#val(v)) { #ok(#leaf(v)) }; case (#put(n, e)) loop { assert false }; case (#array(arr)) { let vals = Buffer.Buffer<(Val, Meta)>(arr.size()); @@ -402,7 +409,6 @@ public class Sequence( case (#ok(list)) { treeOfStream(list) }; } }; - case _ { loop { assert false } }; } }; From b133dcec5d2e253699e89cd0a8ac5f558705c5cd Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 16 Sep 2021 15:16:45 -0600 Subject: [PATCH 12/12] Compiler bug --- examples/sort/Main.mo | 3 +++ examples/sort/Sort.mo | 8 ++++++++ src/data/Sequence.mo | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/sort/Main.mo b/examples/sort/Main.mo index b6ac053..4a5c6ce 100644 --- a/examples/sort/Main.mo +++ b/examples/sort/Main.mo @@ -27,5 +27,8 @@ actor { (#val(#num 16), meta.next()), ] ))))); + + ignore sort.takeLog(); + }; } diff --git a/examples/sort/Sort.mo b/examples/sort/Sort.mo index adbcdcc..af4da3a 100644 --- a/examples/sort/Sort.mo +++ b/examples/sort/Sort.mo @@ -4,6 +4,7 @@ import Meta "../../src/data/Meta"; import R "mo:base/Result"; import L "mo:base/List"; import Text "mo:base/Text"; +import Debug "mo:base/Debug"; /** Incremental sorting example. @@ -41,6 +42,7 @@ public class Sort() { errorEq=func (x:Error, y:Error) : Bool { x == y }; nameHash=Meta.Name.hash; cyclicDependency=func (stack:L.List, name:Name) : Error { + Debug.print(debug_show {stack; name}); assert false; loop { } } }, @@ -86,6 +88,12 @@ public class Sort() { evalRec(exp) }; + public func takeLog() : Engine.Log { + let log = engine.takeLog(); + debug { Debug.print (debug_show log) }; + log + }; + }; } diff --git a/src/data/Sequence.mo b/src/data/Sequence.mo index ab4b43d..a719a84 100644 --- a/src/data/Sequence.mo +++ b/src/data/Sequence.mo @@ -328,7 +328,7 @@ public class Sequence( if (meta.level < treeLevel(tree)) { return resultPair(tree, s) }; - let tailAsStream = switch (haveStream(s)) { + let tailAsStream = switch (haveStream(tail)) { case (#ok(s)) s; case (#err(e)) { return #err(e) } };