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

More pure/Queue improvements #181

Merged
merged 45 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
eb0c5f1
first commit 🤣
ggreif Feb 20, 2025
d2f06d1
`empty`, `isEmpty`, `size` and `singleton` (with docs)
ggreif Feb 20, 2025
2d1aa8d
trying a double-speed runner to halve the list
ggreif Feb 21, 2025
c731df9
`pushFront` and `pushBack`
ggreif Feb 21, 2025
ec8d6aa
`popFront` and `popBack`
ggreif Feb 21, 2025
17f5e01
`fromIter`
ggreif Feb 21, 2025
df17576
`values`, `map`, `filter` and `filterMap`
ggreif Feb 21, 2025
325f3b8
add a debugging invariant
ggreif Feb 22, 2025
efd656d
`compare`
ggreif Feb 22, 2025
6807aa7
tweak
ggreif Feb 22, 2025
3941a94
simplify
ggreif Feb 23, 2025
863a094
Merge branch 'main' into pure/Queue
crusso Feb 25, 2025
cc7e6e2
Add previous `Queue` tests
ggreif Feb 25, 2025
2b12d85
obsolete
ggreif Feb 25, 2025
5aebe31
preliminary `range` for tests' sake
ggreif Feb 25, 2025
b76fd9f
add debug assertions
ggreif Feb 25, 2025
9bcb3a1
adhere to interface
ggreif Feb 25, 2025
f80d229
implement `contains` for pure `List`
ggreif Feb 25, 2025
d4e292a
Implement `Queue.contains` in terms of `List.contains`
ggreif Feb 25, 2025
3135ebc
implement `toText`
ggreif Feb 25, 2025
8141fa0
no more `todo`
ggreif Feb 25, 2025
4118ff7
define `Queue` correctly
ggreif Feb 25, 2025
19288b8
Merge branch 'main' into pure/Queue
ggreif Feb 25, 2025
67af1de
fix error
ggreif Feb 25, 2025
ddd5c5c
use `Nat.range`
ggreif Feb 25, 2025
e999f16
fix
ggreif Feb 25, 2025
de2e29a
there is `Nat.range` now
ggreif Feb 25, 2025
f55f6d8
restore
ggreif Feb 25, 2025
672b5de
add `equal` API to pure `List`
ggreif Feb 25, 2025
cb10fea
simplify
ggreif Feb 25, 2025
781fff7
`npm run validate`
ggreif Feb 25, 2025
7bb25f6
Merge branch 'main' into pure/Queue
ggreif Feb 26, 2025
8f568df
use `trap` instead of `assert`
ggreif Feb 26, 2025
fc238d1
Rewrite pure Queue tests to use 'mo:test'
rvanasa Feb 26, 2025
1cd5fcc
Fix tests
rvanasa Feb 26, 2025
ae290b3
Update API lockfile
rvanasa Feb 26, 2025
d3fdf11
Simplify testing logic
rvanasa Feb 26, 2025
cc92c1b
Remove module name
rvanasa Feb 26, 2025
e8b51fb
Implement mutable Queue toPure() and fromPure()
rvanasa Feb 26, 2025
afd3ea4
Rewrite `pure/Queue` tests to use `test` package (#179)
rvanasa Feb 26, 2025
12e5947
Add tests for Queue.toPure() and Queue.fromPure()
rvanasa Feb 26, 2025
3f6709b
Merge branch 'pure/Queue' of https://github.com/dfinity/new-motoko-ba…
rvanasa Feb 26, 2025
05b7ab4
Rename local variable
rvanasa Feb 26, 2025
a9d3367
Update API lockfile
rvanasa Feb 26, 2025
9fd4f21
Merge branch 'main' into ryan/pure-queue-2
rvanasa Feb 26, 2025
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
59 changes: 53 additions & 6 deletions src/Queue.mo
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,66 @@
import Iter "Iter";
import Order "Order";
import Types "Types";
import PureQueue "pure/Queue";

module {
public type Queue<T> = Types.Queue.Queue<T>;

type Node<T> = Types.Queue.Node<T>;

// public func toPure<T>(queue : Queue<T>) : PureQueue.Queue<T> {
// todo();
// };
/// Converts a mutable queue to an immutable, purely functional queue.
///
/// Example:
/// ```motoko
/// import Queue "mo:base/Queue";
///
/// persistent actor {
/// let queue = Queue.fromIter<Nat>([1, 2, 3].values());
/// let pureQueue = Queue.toPure<Nat>(queue);
/// }
/// ```
///
/// Runtime: O(n)
/// Space: O(n)
/// `n` denotes the number of elements stored in the queue.
public func toPure<T>(queue : Queue<T>) : PureQueue.Queue<T> {
let pureQueue = PureQueue.empty<T>();
let iter = values(queue);
var current = pureQueue;
loop {
switch(iter.next()) {
case null { return current };
case (?val) { current := PureQueue.pushBack(current, val) };
};
};
};

// public func fromPure<T>(queue : PureQueue.Queue<T>) : Queue<T> {
// todo();
// };
/// Converts an immutable, purely functional queue to a mutable queue.
///
/// Example:
/// ```motoko
/// import Queue "mo:base/Queue";
/// import PureQueue "mo:base/pure/Queue";
///
/// persistent actor {
/// let pureQueue = PureQueue.fromIter<Nat>([1, 2, 3].values());
/// let queue = Queue.fromPure<Nat>(pureQueue);
/// }
/// ```
///
/// Runtime: O(n)
/// Space: O(n)
/// `n` denotes the number of elements stored in the queue.
public func fromPure<T>(pureQueue : PureQueue.Queue<T>) : Queue<T> {
let queue = empty<T>();
let iter = PureQueue.values(pureQueue);
loop {
switch(iter.next()) {
case null { return queue };
case (?val) { pushBack(queue, val) };
};
};
};

/// Create a new empty mutable double-ended queue.
///
Expand Down
2 changes: 1 addition & 1 deletion src/pure/Queue.mo
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import List "List";
import Order "../Order";
import Types "../Types";

module Queue {
module {
type List<T> = Types.Pure.List<T>;

/// Double-ended queue data type.
Expand Down
89 changes: 89 additions & 0 deletions test/Queue.test.mo
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Suite "mo:matchers/Suite";
import T "mo:matchers/Testable";
import M "mo:matchers/Matchers";
import Queue "../src/Queue";
import PureQueue "../src/pure/Queue";
import Iter "../src/Iter";
import Nat "../src/Nat";
import Runtime "../src/Runtime";
Expand Down Expand Up @@ -388,6 +389,94 @@ run(
)
);

run(
suite(
"pure queue conversions",
[
test(
"empty to pure",
do {
let queue = Queue.empty<Nat>();
let pureQueue = Queue.toPure(queue);
PureQueue.isEmpty(pureQueue)
},
M.equals(T.bool(true))
),
test(
"empty from pure",
do {
let pureQueue = PureQueue.empty<Nat>();
let queue = Queue.fromPure<Nat>(pureQueue);
Queue.isEmpty(queue)
},
M.equals(T.bool(true))
),
test(
"singleton to pure",
do {
let queue = Queue.singleton<Nat>(1);
let pureQueue = Queue.toPure(queue);
Iter.toArray(PureQueue.values(pureQueue))
},
M.equals(T.array(T.natTestable, [1]))
),
test(
"singleton from pure",
do {
let pureQueue = PureQueue.pushBack(PureQueue.empty(), 1);
let queue = Queue.fromPure<Nat>(pureQueue);
Iter.toArray(Queue.values(queue))
},
M.equals(T.array(T.natTestable, [1]))
),
test(
"multiple elements to pure",
do {
let queue = Queue.fromIter<Nat>([1, 2, 3].vals());
let pureQueue = Queue.toPure(queue);
Iter.toArray(PureQueue.values(pureQueue))
},
M.equals(T.array(T.natTestable, [1, 2, 3]))
),
test(
"multiple elements from pure",
do {
var pureQueue = PureQueue.empty<Nat>();
pureQueue := PureQueue.pushBack(pureQueue, 1);
pureQueue := PureQueue.pushBack(pureQueue, 2);
pureQueue := PureQueue.pushBack(pureQueue, 3);
let queue = Queue.fromPure<Nat>(pureQueue);
Iter.toArray(Queue.values(queue))
},
M.equals(T.array(T.natTestable, [1, 2, 3]))
),
test(
"round trip mutable to pure to mutable",
do {
let original = Queue.fromIter<Nat>([1, 2, 3].vals());
let pureQueue = Queue.toPure(original);
let roundTrip = Queue.fromPure<Nat>(pureQueue);
Iter.toArray(Queue.values(roundTrip))
},
M.equals(T.array(T.natTestable, [1, 2, 3]))
),
test(
"round trip pure to mutable to pure",
do {
var original = PureQueue.empty<Nat>();
original := PureQueue.pushBack(original, 1);
original := PureQueue.pushBack(original, 2);
original := PureQueue.pushBack(original, 3);
let mutableQueue = Queue.fromPure<Nat>(original);
let roundTrip = Queue.toPure(mutableQueue);
Iter.toArray(PureQueue.values(roundTrip))
},
M.equals(T.array(T.natTestable, [1, 2, 3]))
)
]
)
);

// TODO: Use PRNG in new base library
class Random(seed : Nat) {
var number = seed;
Expand Down
19 changes: 2 additions & 17 deletions test/pure/Queue.test.mo
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,16 @@ func iterateBackward<T>(queue : Queue.Queue<T>) : Iter.Iter<T> {
}
};

func toText(queue : Queue.Queue<Nat>) : Text {
var text = "[";
var isFirst = true;
for (element in iterateForward(queue)) {
if (not isFirst) {
text #= ", "
} else {
isFirst := false
};
text #= debug_show (element)
};
text #= "]";
text
};

func frontToText(t : (Nat, Queue.Queue<Nat>)) : Text {
"(" # Nat.toText(t.0) # ", " # toText(t.1) # ")"
"(" # Nat.toText(t.0) # ", " # Queue.toText(t.1, Nat.toText) # ")"
};

func frontEqual(t1 : (Nat, Queue.Queue<Nat>), t2 : (Nat, Queue.Queue<Nat>)) : Bool {
t1.0 == t2.0 and Queue.equal(t1.1, t2.1, Nat.equal)
};

func backToText(t : (Queue.Queue<Nat>, Nat)) : Text {
"(" # toText(t.0) # ", " # Nat.toText(t.1) # ")"
"(" # Queue.toText(t.0, Nat.toText) # ", " # Nat.toText(t.1) # ")"
};

func backEqual(t1 : (Queue.Queue<Nat>, Nat), t2 : (Queue.Queue<Nat>, Nat)) : Bool {
Expand Down
2 changes: 2 additions & 0 deletions validation/api/api.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@
"public func filterMap<T, U>(queue : Queue<T>, project : T -> ?U) : Queue<U>",
"public func forEach<T>(queue : Queue<T>, operation : T -> ())",
"public func fromIter<T>(iter : Iter.Iter<T>) : Queue<T>",
"public func fromPure<T>(pureQueue : PureQueue.Queue<T>) : Queue<T>",
"public func isEmpty<T>(queue : Queue<T>) : Bool",
"public func map<T, U>(queue : Queue<T>, project : T -> U) : Queue<U>",
"public func peekBack<T>(queue : Queue<T>) : ?T",
Expand All @@ -886,6 +887,7 @@
"public type Queue<T>",
"public func singleton<T>(element : T) : Queue<T>",
"public func size<T>(queue : Queue<T>) : Nat",
"public func toPure<T>(queue : Queue<T>) : PureQueue.Queue<T>",
"public func toText<T>(queue : Queue<T>, format : T -> Text) : Text",
"public func values<T>(queue : Queue<T>) : Iter.Iter<T>"
]
Expand Down
Loading