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

Miscellaneous #121

Merged
merged 21 commits into from
Feb 5, 2025
Merged
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
6 changes: 3 additions & 3 deletions src/Array.mo
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ module {
switch (options[nextSome - 1]) {
case (?element) element;
case null {
Prim.trap "Malformed array in filterMap"
Prim.trap "Array.filterMap(): malformed array"
}
}
}
Expand Down Expand Up @@ -397,7 +397,7 @@ module {
element
};
case null {
Prim.trap "Malformed array in mapResults"
Prim.trap "Array.mapResult(): malformed array"
}
}
}
Expand Down Expand Up @@ -729,7 +729,7 @@ module {
///
/// Space: O(length)
public func subArray<T>(array : [T], start : Nat, length : Nat) : [T] {
if (start + length > array.size()) { Prim.trap("Array.subArray()") };
if (start + length > array.size()) { Prim.trap("Array.subArray(): 'start + length' is greater than 'array.size()'") };
Prim.Array_tabulate<T>(length, func i = array[start + i])
};

Expand Down
2 changes: 2 additions & 0 deletions src/Error.mo
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ module {
/// #system_fatal;
/// // Transient error.
/// #system_transient;
/// // Response unknown due to missed deadline.
/// #system_unknown;
/// // Destination invalid.
/// #destination_invalid;
/// // Explicit reject by canister code.
Expand Down
54 changes: 25 additions & 29 deletions src/Float.mo
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
/// let y = 0.3;
///
/// let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis).
/// Float.equalWithin(x, y, epsilon) // => true
/// Float.equal(x, y, epsilon) // => true
/// ```
///
/// * For absolute precision, it is recommened to encode the fraction number as a pair of a Nat for the base
Expand Down Expand Up @@ -491,14 +491,6 @@ module {
/// ```
public let fromInt : Int -> Float = Prim.intToFloat;

/// Returns `x == y`.
/// @deprecated Use `Float.equalWithin()` as this function does not consider numerical errors.
public func equal(x : Float, y : Float) : Bool { x == y };

/// Returns `x != y`.
/// @deprecated Use `Float.notEqualWithin()` as this function does not consider numerical errors.
public func notEqual(x : Float, y : Float) : Bool { x != y };

/// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`.
/// The `epsilon` considers numerical erros, see comment above.
/// Equivalent to `Float.abs(x - y) <= epsilon` for a non-negative epsilon.
Expand All @@ -507,25 +499,25 @@ module {
///
/// Special cases:
/// ```
/// equalWithin(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0`
/// equalWithin(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0`
/// equalWithin(+inf, +inf, epsilon) => true for any `epsilon >= 0.0`
/// equalWithin(-inf, -inf, epsilon) => true for any `epsilon >= 0.0`
/// equalWithin(x, NaN, epsilon) => false for any x and `epsilon >= 0.0`
/// equalWithin(NaN, y, epsilon) => false for any y and `epsilon >= 0.0`
/// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0`
/// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0`
/// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0`
/// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0`
/// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0`
/// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0`
/// ```
///
/// Example:
/// ```motoko
/// import Float "mo:base/Float";
///
/// let epsilon = 1e-6;
/// Float.equalWithin(-12.3, -1.23e1, epsilon) // => true
/// Float.equal(-12.3, -1.23e1, epsilon) // => true
/// ```
public func equalWithin(x : Float, y : Float, epsilon : Float) : Bool {
public func equal(x : Float, y : Float, epsilon : Float) : Bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

@luc-blaeser are you ok with renaming equalWIthin (and notEqualWithin) to equal and notEqual?
Also another option might be to make both of these curried functions, that take the epsilon as a first argument and return an equality/inequality function of the usual binary type:

  public func equalWithin(epsilon: Float) : (x : Float, y : Float) - > Bool =
       func(x : Float, y: float)  { ... epsilon ...  }
  }
 (and similarly for nonEqualWithin 

Copy link
Collaborator Author

@rvanasa rvanasa Feb 4, 2025

Choose a reason for hiding this comment

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

The motivation here is for API uniformity. I suppose we could curry the arguments, although I think equal(x, y, epsilon) is more consistent with how we handle the compare and equal functions for data structures.

On the other hand, it could be worth doing something different to reduce possible confusion about the argument order, given the equal(Float, Float, Float) signature.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, I thinking about the situation where you want to pass the equality function as a first-class argument (which is why we define it for convenience...)

I don't have a strong opinion...

if (not (epsilon >= 0.0)) {
// also considers NaN, not identical to `epsilon < 0.0`
Prim.trap("epsilon must be greater or equal 0.0")
Prim.trap("Float.equal(): epsilon must be greater or equal 0.0")
};
x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal
};
Expand All @@ -538,23 +530,27 @@ module {
///
/// Special cases:
/// ```
/// notEqualWithin(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0`
/// notEqualWithin(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0`
/// notEqualWithin(+inf, +inf, epsilon) => false for any `epsilon >= 0.0`
/// notEqualWithin(-inf, -inf, epsilon) => false for any `epsilon >= 0.0`
/// notEqualWithin(x, NaN, epsilon) => true for any x and `epsilon >= 0.0`
/// notEqualWithin(NaN, y, epsilon) => true for any y and `epsilon >= 0.0`
/// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0`
/// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0`
/// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0`
/// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0`
/// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0`
/// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0`
/// ```
///
/// Example:
/// ```motoko
/// import Float "mo:base/Float";
///
/// let epsilon = 1e-6;
/// Float.notEqualWithin(-12.3, -1.23e1, epsilon) // => false
/// Float.notEqual(-12.3, -1.23e1, epsilon) // => false
/// ```
public func notEqualWithin(x : Float, y : Float, epsilon : Float) : Bool {
not equalWithin(x, y, epsilon)
public func notEqual(x : Float, y : Float, epsilon : Float) : Bool {
Copy link
Contributor

@crusso crusso Feb 5, 2025

Choose a reason for hiding this comment

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

Do we even want notEqual, since its the literal negation of equal @luc-blaeser and we don't provide notEqual for other primitive types.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Seems reasonable to remove this function. I'll merge and open another PR to start working on the merge conflicts in parallel.

if (not (epsilon >= 0.0)) {
// also considers NaN, not identical to `epsilon < 0.0`
Prim.trap("Float.notEqual(): epsilon must be greater or equal 0.0")
};
not (x == y or abs(x - y) <= epsilon)
};

/// Returns `x < y`.
Expand Down Expand Up @@ -632,8 +628,8 @@ module {
/// Defines a total order of `x` and `y` for use in sorting.
///
/// Note: Using this operation to determine equality or inequality is discouraged for two reasons:
/// * It does not consider numerical errors, see comment above. Use `equalWithin(x, y, espilon)` or
/// `notEqualWithin(x, y, epsilon)` to test for equality or inequality, respectively.
/// * It does not consider numerical errors, see comment above. Use `equal(x, y, espilon)` or
/// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively.
/// * `NaN` are here considered equal if their sign matches, which is different to the standard equality
/// by `==` or when using `equal()` or `notEqual()`.
///
Expand Down
25 changes: 0 additions & 25 deletions src/Hash.mo
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,4 @@ module {
ha == hb
};

/// Computes a hash from the least significant 32-bits of `n`, ignoring other bits.
Copy link
Contributor

@crusso crusso Feb 4, 2025

Choose a reason for hiding this comment

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

If we aren't going to even provide any hash based data structures (are we?) I'd be tempted to get rid of this whole module Hash.mo. length seemes misnamed (should be maxBitIndex or something) and bit is probably an implementation detail of Trie.mo which uses the hash bits to index into a radix tree (IIRC). Might be worth checking if pos/length are used anywhere else.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Opened a separate PR for this: #129.

/// @deprecated For large `Nat` values consider using a bespoke hash function that considers all of the argument's bits.
public func hash(n : Nat) : Hash {
let j = Prim.intToNat32Wrap(n);
hashNat8([
j & (255 << 0),
j & (255 << 8),
j & (255 << 16),
j & (255 << 24)
])
};

func hashNat8(key : [Hash]) : Hash {
var hash : Nat32 = 0;
for (natOfKey in key.vals()) {
hash := hash +% natOfKey;
hash := hash +% hash << 10;
hash := hash ^ (hash >> 6)
};
hash := hash +% hash << 3;
hash := hash ^ (hash >> 11);
hash := hash +% hash << 15;
return hash
};

}
27 changes: 0 additions & 27 deletions src/Int.mo
Original file line number Diff line number Diff line change
Expand Up @@ -147,33 +147,6 @@ module {
if (x < y) { y } else { x }
};

// this is a local copy of deprecated Hash.hashNat8 (redefined to suppress the warning)
private func hashNat8(key : [Nat32]) : Hash.Hash {
var hash : Nat32 = 0;
for (natOfKey in key.vals()) {
hash := hash +% natOfKey;
hash := hash +% hash << 10;
hash := hash ^ (hash >> 6)
};
hash := hash +% hash << 3;
hash := hash ^ (hash >> 11);
hash := hash +% hash << 15;
return hash
};

/// Computes a hash from the least significant 32-bits of `i`, ignoring other bits.
/// @deprecated For large `Int` values consider using a bespoke hash function that considers all of the argument's bits.
public func hash(i : Int) : Hash.Hash {
// CAUTION: This removes the high bits!
let j = Prim.int32ToNat32(Prim.intToInt32Wrap(i));
hashNat8([
j & (255 << 0),
j & (255 << 8),
j & (255 << 16),
j & (255 << 24)
])
};

/// Equality function for Int types.
/// This is equivalent to `x == y`.
///
Expand Down
2 changes: 1 addition & 1 deletion src/Iter.mo
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ module {
count,
func(_) {
switch (current) {
case null Runtime.trap("Node must not be null");
case null Runtime.trap("Iter.toArray(): node must not be null");
case (?node) {
current := node.next;
node.value
Expand Down
2 changes: 1 addition & 1 deletion src/Map.mo
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ module {
public func add<K, V>(map : Map<K, V>, compare : (K, K) -> Order.Order, key : K, value : V) : () {
switch (put(map, compare, key, value)) {
case null {};
case (?value) Runtime.trap("Key is already present")
case (?value) Runtime.trap("Map.add(): key is already present")
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/Text.mo
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ module {
func _ {
switch (cs.next()) {
case (?c) { c };
case null { Prim.trap("Text.toArray") }
case null { Prim.trap("Text.toArray()") }
}
}
)
Expand Down
39 changes: 27 additions & 12 deletions src/Timer.mo
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
/// Timers for one-off or periodic tasks.
/// Timers for one-off or periodic tasks. Applicable as part of the default mechanism.
/// If `moc` is invoked with `-no-timer`, the importing will fail. Furthermore, if passed `--trap-on-call-error`, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer.
///
/// Note: If `moc` is invoked with `-no-timer`, the importing will fail.
/// Note: The resolution of the timers is in the order of the block rate,
/// so durations should be chosen well above that. For frequent
/// canister wake-ups the heatbeat mechanism should be considered.

/// The resolution of the timers is similar to the block rate,
/// so durations should be chosen well above that. For frequent
/// canister wake-ups, consider using the [heartbeat](https://internetcomputer.org/docs/current/motoko/main/writing-motoko/heartbeats) mechanism; however, when possible, canisters should prefer timers.
///
/// The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`.
///
/// Timers are _not_ persisted across upgrades. One possible strategy
/// to re-establish timers after an upgrade is to use stable variables
/// in the `post_upgrade` hook and distill necessary timer information
/// from there.
///
/// Using timers for security (e.g., access control) is strongly discouraged.
/// Make sure to inform yourself about state-of-the-art dapp security.
/// If you must use timers for security controls, be sure
/// to consider reentrancy issues as well as the vanishing of timers on upgrades
/// and reinstalls.
///
/// For further usage information for timers on the IC, please consult
/// [the documentation](https://internetcomputer.org/docs/current/developer-docs/backend/periodic-tasks#timers-library-limitations).
import { setTimer = setTimerNano; cancelTimer = cancel } = "mo:⛔";
import Nat64 = "Nat64";
import Time "Time";
import Nat64 "Nat64";

module {

public type Duration = { #seconds : Nat; #nanoseconds : Nat };
public type TimerId = Nat;

/// Installs a one-off timer that upon expiration after given `duration` executes the future `job()`.
/// Installs a one-off timer that upon expiration after given duration `d`
/// executes the future `job()`.
///
/// ```motoko no-repl
/// let now = Time.now();
Expand All @@ -28,7 +43,7 @@ module {
setTimerNano<system>(Nat64.fromNat(Time.toNanoseconds duration), false, job)
};

/// Installs a recurring timer that upon expiration after given `duration`
/// Installs a recurring timer that upon expiration after given duration `d`
/// executes the future `job()` and reinserts itself for another expiration.
///
/// Note: A duration of 0 will only expire once.
Expand All @@ -47,8 +62,8 @@ module {
/// and not recognised `id`s nothing happens.
///
/// ```motoko no-repl
/// func deleteAppt(appt : Appointment) {
/// cancelTimer (appt.reminder);
/// func deleteAppointment(appointment : Appointment) {
/// cancelTimer (appointment.reminder);
/// // ...
/// };
/// ```
Expand Down
4 changes: 2 additions & 2 deletions src/VarArray.mo
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ module {
switch (options[nextSome - 1]) {
case (?element) element;
case null {
Prim.trap "Malformed array in filterMap"
Prim.trap "VarArray.filterMap(): malformed array"
}
}
}
Expand Down Expand Up @@ -486,7 +486,7 @@ module {
element
};
case null {
Prim.trap "Malformed array in mapResults"
Prim.trap "VarArray.mapResults(): malformed array"
}
}
}
Expand Down
Loading