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

Adds some documentation/examples #51

Merged
merged 10 commits into from
Jun 23, 2020
87 changes: 82 additions & 5 deletions src/Iter.mo
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,45 @@ import Array "Array";
import List "List";

module {
public type Iter<T> = {next : () -> ?T};
/// An iterator that produces values of type `T`. Calling `next` returns
/// `null` when iteration is finished.
///
/// Iterators are inherently stateful. Calling `next` "consumes" a value from
/// the Iterator that cannot be put back, so keep that in mind when sharing
/// iterators between consumers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so keep that in mind when sharing iterators between consumers.

I'm more confused than helped by this warning.

Are you effectively saying that "an iterator is a mutable object; when shared, multiple users calling next will interfere"?

public type Iter<T> = { next : () -> ?T };
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved

/// Creates an iterator that produces all `Nat`'s from `x` to `y` including
/// both of the bounds.
/// ```
/// let iter = range(1, 3);
/// assertEqual(?1, iter.next());
/// assertEqual(?2, iter.next());
/// assertEqual(?3, iter.next());
/// assertEqual(null, iter.next());
/// ```
public class range(x : Nat, y : Int) {
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
var i = x;
public func next() : ?Nat { if (i > y) null else {let j = i; i += 1; ?j} };
};

/// Like [`range`](#value.range) but produces the values in the opposite
/// order.
public class revRange(x : Int, y : Int) {
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
var i = x;
public func next() : ?Int { if (i < y) null else {let j = i; i -= 1; ?j} };
};

/// Calls a function `f` on every value produced by an iterator and discards
/// the results. If you're looking to keep these results use
/// [`map`](#value.map).
/// ```
/// var sum = 0;
/// apply(range(1, 3), func(x : Nat) {
/// sum += x;
/// });
/// assertEquals(6, sum)
/// ```
public func apply<A>(
xs : Iter<A>,
f : (A, Nat) -> ()
Expand All @@ -35,14 +62,25 @@ module {
};
};

func size<A>(xs : Iter<A>) : Nat {
/// Consumes an iterator and counts how many elements were produced
/// (discarding them in the process).
public func size<A>(xs : Iter<A>) : Nat {
var len = 0;
apply<A>(xs, func (x, i) { len += 1; });
len;
};

/// Takes a function and an iterator and creates a new iterator by applying
/// the function to every element produced by the old iterator.
/// ```
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
/// let iter = range(1, 3);
/// let mappedIter = map(iter, func (x : Nat) : Nat { x * 2 });
/// assertEqual(?2, mappedIter.next());
/// assertEqual(?4, mappedIter.next());
/// assertEqual(?6, mappedIter.next());
/// assertEqual(null, mappedIter.next());
/// ```
public func map<A, B>(xs : Iter<A>, f : A -> B) : Iter<B> = object {
var i = 0;
public func next() : ?B {
label l loop {
switch (xs.next()) {
Expand All @@ -53,43 +91,82 @@ module {
break l;
};
};
i += 1;
continue l;
};
null;
};
};

/// Creates an iterator that produces an infinite sequence of `x`.
/// ```
/// let iter = make(10);
/// assertEquals(?10, iter.next())
/// assertEquals(?10, iter.next())
/// assertEquals(?10, iter.next())
/// // ...
/// ```
public func make<A>(x : A) : Iter<A> = object {
public func next() : ?A {
?x;
};
};

/// Creates an iterator that produces the elements of an Array.
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
/// ```
/// let iter = fromArray([1, 2, 3]);
/// assertEquals(?1, iter.next())
/// assertEquals(?2, iter.next())
/// assertEquals(?3, iter.next())
/// assertEquals(null, iter.next())
/// ```
public func fromArray<A>(xs : [A]) : Iter<A> {
fromList<A>(List.fromArray<A>(xs));
var ix : Nat = 0;
let size = xs.len();
object {
public func next() : ?A {
if (ix >= size) {
return null
} else {
let res = ?(xs[ix]);
ix += 1;
return res
}
}
}
};

/// Like [`fromArray`](#value.fromArray) but for Arrays with mutable elements.
/// Captures the elements of the Array at the time the iterator is created, so
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
/// further modifications won't be reflected in the iterator.
public func fromArrayMut<A>(xs : [var A]) : Iter<A> {
fromArray<A>(Array.freeze<A>(xs));
};

/// Like [`fromArray`](#value.fromArray) but for Lists.
public func fromList<A>(xs : List.List<A>) : Iter<A> {
List.toArray<A>(xs).vals();
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
};

/// Consumes an iterator and collects its produced elements in an Array.
/// ```
/// let iter = range(1, 3);
/// assertEquals([1, 2, 3], toArray(iter));
/// ```
public func toArray<A>(xs : Iter<A>) : [A] {
List.toArray<A>(toList<A>(xs));
};

/// Like [`toArray`](#value.toArray) but for Arrays with mutable elements.
public func toArrayMut<A>(xs : Iter<A>) : [var A] {
Array.thaw<A>(toArray<A>(xs));
};

/// Like [`toArray`](#value.toArray) but for Lists.
public func toList<A>(xs : Iter<A>) : List.List<A> {
toListWithSize<A>(xs).list;
};

/// Deprecate?
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
public func toListWithSize<A>(
xs : Iter<A>,
) : ({
Expand Down
82 changes: 61 additions & 21 deletions src/Option.mo
Original file line number Diff line number Diff line change
@@ -1,45 +1,62 @@
/// Optional values
/// Typesafe nulls
///
/// Optional values can be seen as a typesafe `null`. A value of type ?(Int) can
/// be constructed with either `null` or `?(42)`. The simplest way to get at
/// the contents of an optional is to use pattern matching:
///
/// ```motoko
/// let optionalInt1 : ?(Int) = ?(42);
/// let optionalInt2 : ?(Int) = null;
///
/// let int1orZero : Int = switch(optionalInt1) {
/// case null 0;
/// case ?(int) int;
/// };
/// assertEquals(int1OrZero, 42);
///
/// let int2orZero : Int = switch(optionalInt2) {
/// case null 0;
/// case ?(int) int;
/// };
/// assertEquals(int2OrZero, 0);
/// ```
///
/// The functions in this module capture some common operations when working
/// with optionals that can be more succinct than using pattern matching.

import P "Prelude";

module {

/// Returns true if the argument is not `null`.
public func isSome(x : ?Any) : Bool =
switch x {
case null false;
case _ true;
};

/// Returns true if the argument is `null`.
public func isNull(x : ?Any) : Bool =
switch x {
case null true;
case _ false;
};

/// Unwraps an optional value, with a default value, i.e. `unwrap(?x, d) = x` and `unwrap(null, d) = d`.
/// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and
/// `get(null, d) = d`.
public func get<T>(x : ?T, default : T) : T =
switch x {
case null { default };
case (?x_) x_;
};

/// Unwraps an optional value using a function, or returns the default, i.e. `option(?x, f, d) = f x` and `option(null, f, d) = d`.
/// Unwraps an optional value using a function, or returns the default, i.e.
/// `option(?x, f, d) = f x` and `option(null, f, d) = d`.
public func getMapped<A, B>(x : ?A, f : A -> B, default : B) : B =
switch x {
case null { default };
case (?x_) f(x_);
};

/// Applies a function to the wrapped value.
/// Applies a function to the wrapped value. `null`'s are left untouched.
/// ```
/// map(f, ?(x)) = ?(f(x))
/// map(f, null) = null
/// ```
public func map<A, B>(f : A->B, x : ?A) : ?B =
switch x {
case null null;
case (?x_) ?f(x_);
};

/// Applies an optional function to an optional value. Returns `null` if at least one of the arguments is `null`.
/// Applies an optional function to an optional value. Returns `null` if at
/// least one of the arguments is `null`.
public func apply<A, B>(x : ?A, f : ?(A -> B)) : ?B {
switch (f, x) {
case (?f_, ?x_) {
Expand All @@ -51,9 +68,10 @@ public func apply<A, B>(x : ?A, f : ?(A -> B)) : ?B {
};
};

/// Applies an function to an optional value. Returns `null` if the argument is `null`, or the function returns `null`.
/// Applies a function to an optional value. Returns `null` if the argument is
/// `null`, or the function returns `null`.
///
/// NOTE: Together with <<Option_pure,`pure`>>, this forms a “monad”.
/// NOTE: Together with [`make`](#value.make), this forms a “monad”.
public func chain<A, B>(x : ?A, f : A -> ?B) : ?B {
switch(x) {
case (?x_) {
Expand All @@ -66,15 +84,37 @@ public func chain<A, B>(x : ?A, f : A -> ?B) : ?B {
};

/// Given an optional optional value, removes one layer of optionality.
/// ```
/// flatten(?(?(42)))) = ?(42)
/// flatten(?(null))) = null
/// flatten(null)) = null
/// ```
public func flatten<A>(x : ??A) : ?A {
chain<?A, A>(x, func (x_ : ?A) : ?A {
x_;
});
};

/// Creates an optional value from a definite value.
/// ```
/// make(42) = ?(42)
/// ```
public func make<A>(x: A) : ?A = ?x;

/// Returns true if the argument is not `null`.
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
public func isSome(x : ?Any) : Bool =
switch x {
case null false;
case _ true;
};

/// Returns true if the argument is `null`.
kritzcreek marked this conversation as resolved.
Show resolved Hide resolved
public func isNull(x : ?Any) : Bool =
switch x {
case null true;
case _ false;
};

/// Asserts that the value is not `null`; fails otherwise.
/// Deprecated.
public func assertSome(x : ?Any) =
Expand Down