diff options
Diffstat (limited to 'std/prelude')
-rw-r--r-- | std/prelude/format.pk | 53 | ||||
-rw-r--r-- | std/prelude/iterators.pk | 38 | ||||
-rw-r--r-- | std/prelude/options.pk | 101 | ||||
-rw-r--r-- | std/prelude/results.pk | 136 |
4 files changed, 328 insertions, 0 deletions
diff --git a/std/prelude/format.pk b/std/prelude/format.pk new file mode 100644 index 0000000..3adb68b --- /dev/null +++ b/std/prelude/format.pk @@ -0,0 +1,53 @@ +## std.format: Niceties around printing and debugging. +## This module is imported by default. + +## The Display interface. Any type implementing `str` is printable. +## Any type that is Display must necessarily also implement Debug. +pub type Display = interface + str(Self): str + dbg(Self): str + +## The Debug interface. Broadly implemented for every type with compiler magic. +## Types can (and should) override the generic implementations. +pub type Debug = interface + dbg(Self): str + +## Prints all of its arguments to the command line. +pub func print(params: varargs[Display]) = + stdout.write(params.map(x => x.str).join(" "), "\n") + +## Prints all of its arguments to the command line, in Debug form. +## Note: this function is special! does not count as a side effect +pub func dbg(params: varargs[Debug]) = + stdout.write(params.map(x => x.dbg).join(" "), "\n") + +## A dummy implementation of the Display interface for strings. +pub func str(self: str): str = self +## An implementation of the Debug interface for strings. +pub func dbg(self: str): str = "\"" & self & "\"" + +## An implementation of the Debug interface for all structs. +## Uses the special `struct` typeclass. +pub func dbg(self: struct): str = + "{{}}".fmt(self.fields.map((key, val) => key & ":" & val.dbg)) + +## An implementation of the Debug interface for all tuples. +## Uses the special `tuple` typeclass. +pub func dbg(self: tuple): str = + "({})".fmt(self.fields.map((key, val) => + key.map(x => x & ":").get_or("") & val.dbg).join(", ")) + +## An implementation of the Debug interface for all arrays and lists. +pub func dbg[T: Debug](self: Iter[T]): str = + "[{}]".fmt(self.map(x => x.str).join(", ")) + +## The fmt macro. Builds a formatted string from its arguments. +pub macro fmt(self: static[str], args: varargs[Display]): str = + let parts = self.split("{}") + if parts.len != args.len + 1: + macro_error("wrong number of arguments") + use std.ast + result = parts.get(0)! + for i, arg in args: + result &= quote(`parts` & str(`arg`) &) # fixme + result &= parts.last()! diff --git a/std/prelude/iterators.pk b/std/prelude/iterators.pk new file mode 100644 index 0000000..224460f --- /dev/null +++ b/std/prelude/iterators.pk @@ -0,0 +1,38 @@ +## std.iterators: The Iter interface and associated functions. +## This module is imported by default. + +## The Iter interface. Any type implementing `next()` is iterable. +pub type Iter[T] = interface + next(mut Self): Option[T] + +# todo: useful functions for an iterator +# https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods + +pub func advance_by[T](self: Iter[T], n: uint): Result[T, ...] = + for i in 0 .. n: + if self.next().is_none(): + return Error(...) + Okay + +pub func get[T](self: Iter[T], at: uint): Option[T] + self.advance_by(at-1).ok? + self.next() + +# todo: implement iter[T](self: ...): Iter[T] funcs +# todo: efficient functional methods + +## The Peek interface. Any type implementing `Iter`, `peek`, and `peek_nth` is peekable. +pub type Peek[T] = interface + next(mut Self): Option[T] + peek(mut Self): Option[T] + peek_nth(mut Self, int): Option[T] + +# todo: implement peek[T](self: Iter[T]): Peek[T] +# todo: implement Peekable struct +# https://github.com/LukeMathWalker/multipeek/blob/main/src/lib.rs + +## We don't want a Countable. It's not terribly useful. +# pub type Countable[T] = interface +# next(mut Self): Option[T] +# len(Self): uint +# get(Self, uint): Option[T] diff --git a/std/prelude/options.pk b/std/prelude/options.pk new file mode 100644 index 0000000..4f6cef2 --- /dev/null +++ b/std/prelude/options.pk @@ -0,0 +1,101 @@ +## std.options: Optional types. +## This module is imported by default. + +use std.format + +## The `Option` type. +## A type that represents either the presence or absence of a value. +pub type Option[T] = union + Some(T) + None + +## Checks if a type is present within an `Option` type. +pub func is_some[T](self: Option[T]): bool = + self of Some(_) +## Checks if a type is not present within an `Option` type. +pub func is_none[T](self: Option[T]): bool = + self of None + +## Converts an `Option[T]` to a `Result[T, E]` given a user-provided error. +pub func err[T, E](self: Option[T], error: E): Result[T, E] = + if self of Some(x): + Okay(x) + else: + Error(error) + +## Applies a function to `T`, if it exists. +pub func map[T, U](self: Option[T], fn: T -> U): Option[U] = + if self of Some(x): + Some(fn(x)) + else: + None +## Converts `T` to a `None`, if `fn` returns false and it exists. +pub func filter[T](self: Option[T], fn: T -> bool): Option[T] = + if self of Some(x) and fn(x): + Some(x) + else: + None + +## Applies a function to T, if it exists. Equivalent to `self.map(fn).flatten`. +pub func flatmap[T, U](self: Option[T], fn: T -> Option[U]): Option[U] = + if self of Some(x): + fn(x) + else: + None +## Converts from Option[Option[T]] to Option[T]. +pub func flatten[T](self: Option[Option[T]]): Option[T] = + match self + of Some(Some(x)): + Some(x) + of _: + None + +## Returns the inner value or a default. +pub func get_or[T](self: Option[T], default: T): T = + if self of Some(x): x + else: default + +## A dummy type signalling the empty value +type EmptyType = distinct unit + +## Directly accesses the inner value. Throws an exception if None. +pub func ![T](self: Option[T]): T = + if self of Some(x): x + else: raise EmptyType + +## Indirect access. Propagates `None`. +## Parsed by compiler magic. +pub macro ?[T](self: Option[T]) = + quote: + match `self` + of Some(x): x + of None: return None + +## Syntactic sugar for optional type declarations. +pub macro ?(T: type) = + quote: + Option[`T`] + +## Overloads the `==` operation for use on Options. +pub func ==[T](a, b: Option[T]): bool = + match (a, b) + of (Some(x), Some(y)): + x == y + of _: + false + +## Overloads the `str()` function for use on Options. +pub func str[T](self: Option[T]): str = + if self of Some(x): + fmt("Some({})", x.str) + else: + "None" + +examples: + let x = Some(42) + if x of Some(y): + assert x! == y + +# references: +# https://nim-lang.github.io/Nim/options.html +# https://doc.rust-lang.org/std/option/enum.Option.html diff --git a/std/prelude/results.pk b/std/prelude/results.pk new file mode 100644 index 0000000..1209b40 --- /dev/null +++ b/std/prelude/results.pk @@ -0,0 +1,136 @@ +## std.results: Result types. +## This module is imported by default. + +use std[options, format] + +## The Result type. Represents either success or failure. +pub type Result[T, E] = union + Okay(T) + Error(E) + +## The Err interface. Useful for dynamically dispatching errors. +pub type Err = interface + str(Self): str + dbg(Self): str + +## A `Result` type that uses dynamically dispatched errors. +## The `Error` may be any type implementing `Err`. +pub type Result[T] = Result[T, ref Err] +## A `Result` type that only checks for success. +## Does not contain a value. +pub type Success[E] = Result[Unit, E] + +## Checks if a `Result` type was successful. +pub func is_ok[T, E](self: Result[T, E]): bool = + self of Okay(_) +## Checks if a `Result` type was not successful. +pub func is_err[T, E](self: Result[T, E]): bool = + self of Error(_) + +## Converts from a `Result[T, E]` to an `Option[T]`. +pub func ok[T, E](self: Result[T, E]): Option[T] = + if self of Okay(x): + Some(x) + else: + None() +## Converts from a `Result[T, E]` to an `Option[E]`. +pub func err[T, E](self: Result[T, E]): Option[E] = + if self of Error(x): + Some(x) + else: + None() + +## Applies a function to `T`, if self is `Okay`. +pub func map[T, E, U](self: Result[T, E], fn: T -> U): Result[U, E] = + match self + of Okay(x): + Okay(fn(x)) + of Error(e): + Error(e) +## Applies a function to `E`, if self is `Error`. +pub func map_err[T, E, F](self: Result[T, E], fn: E -> F): Result[T, F] = + match self + of Error(e): + Error(fn(e)) + of Okay(x): + Okay(x) + +## Applies a function to `T`, if it exists. Equivalent to `self.map(fn).flatten`. +pub func flatmap[T, E, U](self: Result[T, E], fn: T -> Result[U, E]): Result[U, E] = + match self + of Okay(x): + fn(x) + of Error(e): + Error(e) +## Converts from a `Result[Result[T, E], E]` to a `Result[T, E]`. +pub func flatten[T, E](self: Result[Result[T, E], E]): Result[T, E] = + match self + of Okay(Okay(x)): + Okay(x) + of Okay(Error(e)), Error(e): + Error(e) + +## Transposes a `Result[Option[T], E]` to an `Option[Result[T, E]]`. +pub func transpose[T, E](self: Result[Option[T], E]): Option[Result[T, E]] = + match self + of Okay(Some(x)): + Some(Okay(x)) + of Okay(None()), Error(_): + None() +## Transposes an `Option[Result[T, E]]` to a `Result[Option[T], E]`. Takes a default error. +pub func transpose[T, E](self: Option[Result[T, E]], error: E): Result[Option[T], E] = + match self + of Some(Okay(x)): + Okay(Some(x)) + of Some(Error(e)): + Error(e) + of None(): + Error(error) + +## Returns the inner value or a default. +pub func get_or[T, E](self: Result[T, E], default: T): T = + if self of Okay(x): x + else: default + +## Directly accesses the inner value. Throws an exception if `Error`. +pub func ![T, E](self: Result[T, E]): T = + match self + of Okay(x): x + of Error(e): raise e +## Directly accesses the inner error. Throws an exception of type T if `Okay`. +pub func get_err[T, E](self: Result[T, E]): E = + match self + of Error(e): e + of Okay(x): raise x + +## Indirect access. Propagates `Error`. +pub macro ?[T, E](self: Result[T, E]) = + quote: + match `self` + of Okay(x): x + of Error(e): return Error(e) + +## Overloads the `==` operation for use on Results. +pub func ==[T, E, F](a: Result[T, E], b: Result[T, F]): bool = + match (a, b) + of (Okay(x), Okay(y)): + x == y + of _: + false + +## Overloads the `str()` function for use on Results. +pub func str[T, E](self: Result[T, E]): str = + match self + of Some(x): + fmt("Okay({})", x.str) + of Error(e): + fmt("Error({})", e.str) + +examples: + let x: Error("fuck") = Okay(42) + func idk: Result[int, string] + +# references: +# https://doc.rust-lang.org/std/result/enum.Result.html +# https://github.com/arnetheduck/nim-results +# https://github.com/codex-storage/questionable |