Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feature: Incremental sequences, sorting. #33

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
# environment variables to set for dfx
env:
DFX_VERSION: 0.8.0
DFX_VERSION: 0.8.1
steps:
- uses: actions/checkout@v2
- name: Install
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"canisters": {
"Calc": {
"main": "examples/calc/Main.mo"
},
"Sort": {
"main": "examples/sort/Main.mo"
}
},
"defaults": {
Expand Down
37 changes: 37 additions & 0 deletions examples/sort/Main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Meta "../../src/data/Meta";
import Sort "Sort";

actor {
var sort = Sort.Sort();
let meta = Meta.Counter();
public query func test() : async () {
let r =
sort.eval(
#put(#text "output",
#seq(#treeOfStream(#streamOfArray(#text "input",
#array([
(#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()),
*/
])
)))));
let r1 =
sort.eval(#seq(#treeGet(#at "output")));

sort.printLog()

};
}
99 changes: 99 additions & 0 deletions examples/sort/Sort.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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";
import Debug "mo:base/Debug";

/**
Incremental sorting example.
Uses adapton for representing stream thunks and caching them as data changes.
*/
module {

public type Name = Meta.Name;

public type Val = {
#unit;
#num : Nat;
#seq : Seq.Val<Val>;
};

public type Error = {
#typeMismatch;
#seq : Seq.Error<Val>;
};

public type Exp = {
#unit;
#num : Nat;
#seq : Seq.Exp<Val>;
};

public class Sort() {
/* -- cache implementation, via adapton package -- */
public var engine : Engine.Engine<Name, Val, Error, Exp> =
Engine.Engine<Name, Val, Error, Exp>(
{
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:Name) : Error {
Debug.print(debug_show {stack; name});
assert false; loop { }
}
},
true // logging
);
public var engineIsInit = false;

public var seq = Seq.Sequence<Val, Error, Exp>(
engine,
{
valMax = func(v1 : Val, v2 : Val) : R.Result<Val, Error> {
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<Val> =
switch v { case (#seq(s)) ?s; case _ null };
putVal = func(v : Seq.Val<Val>) : Val = #seq(v);
putExp = func(e : Seq.Exp<Val>) : Exp = #seq(e);
getExp = func(e : Exp) : ?Seq.Exp<Val> =
switch e { case (#seq(e)) ?e; case _ null };
putError = func(e : Seq.Error<Val>) : Error = #seq(e);
getError = func(e : Error) : ?Seq.Error<Val> =
switch e { case (#seq(e)) ?e; case _ null };
}
);

func evalRec(exp : Exp) : R.Result<Val, Error> {
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<Val, Error> {
if (not engineIsInit) {
engine.init({eval=evalRec});
engineIsInit := true
};
evalRec(exp)
};

public func printLog() {
let log = engine.takeLog();
// compiler bug here, it seems:
debug { Debug.print (debug_show log) }
};

};

}
6 changes: 6 additions & 0 deletions src/Engine.mo
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ module {
context.logOps.end(tag)
};

public func nest<X>(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<Name, G.PutError> {
logBegin();
let newRefNode : G.Ref<Name, Val, Error, Closure> = {
Expand Down
64 changes: 64 additions & 0 deletions src/data/Meta.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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";
import Nat "mo:base/Nat";
import Nat32 "mo:base/Nat32";

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;
};

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 {
switch n {
case (#none) Text.hash "none";
case (#text(t)) Text.hash t;
case _ Text.hash "?"; // to do
}
};
};

public class Counter() {
var counter : Nat = 1;
public func next () : Meta {
let level = Level.ofNat(counter);
let name = #nat counter;
let meta = {level; name};
counter += 1;
meta
};
};

}
13 changes: 13 additions & 0 deletions src/data/README.md
Original file line number Diff line number Diff line change
@@ -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.

Loading