aboutsummaryrefslogtreecommitdiff
path: root/std/prelude
diff options
context:
space:
mode:
authorJJ2023-12-30 00:03:06 +0000
committerJJ2023-12-30 00:03:06 +0000
commitb8445b011d9b2a287af0e014998c42525a7fd315 (patch)
tree1b1008c2df5902fae12f40a394113a6453da2a4b /std/prelude
parent774a35ae21dada36af48ae32c862b22587fba107 (diff)
std: restructure, refactor std.ast
Diffstat (limited to 'std/prelude')
-rw-r--r--std/prelude/format.pk53
-rw-r--r--std/prelude/iterators.pk38
-rw-r--r--std/prelude/options.pk101
-rw-r--r--std/prelude/results.pk136
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