bugfixes and improvements
This commit is contained in:
parent
d64467ef6d
commit
07c6d23587
229
prelude.ld
229
prelude.ld
|
@ -9,26 +9,26 @@
|
|||
|
||||
& some forward declarations
|
||||
& TODO: fix this so that we don't need (as many of) them
|
||||
fn first
|
||||
fn append
|
||||
fn some?
|
||||
fn update!
|
||||
fn string
|
||||
fn join
|
||||
fn neg?
|
||||
fn atan/2
|
||||
fn mod
|
||||
fn assoc & ?
|
||||
fn dict
|
||||
fn get
|
||||
fn unbox
|
||||
fn store!
|
||||
fn turn/rad
|
||||
fn deg/rad
|
||||
fn floor
|
||||
fn and
|
||||
fn append
|
||||
fn apply_command
|
||||
fn assoc & ?
|
||||
fn atan/2
|
||||
fn deg/rad
|
||||
fn dict
|
||||
fn first
|
||||
fn floor
|
||||
fn get
|
||||
fn join
|
||||
fn mod
|
||||
fn neg?
|
||||
fn some?
|
||||
fn state/call
|
||||
fn store!
|
||||
fn string
|
||||
fn turn/rad
|
||||
fn unbox
|
||||
fn update!
|
||||
|
||||
& the very base: know something's type
|
||||
fn type {
|
||||
|
@ -36,6 +36,30 @@ fn type {
|
|||
(x) -> base :type (x)
|
||||
}
|
||||
|
||||
& some helper type functions
|
||||
fn coll? {
|
||||
"Returns true if a value is a collection: dict, list, tuple, or set."
|
||||
(coll as :dict) -> true
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(coll as :set) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn ordered? {
|
||||
"Returns true if a value is an indexed collection: list or tuple."
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn assoc? {
|
||||
"Returns true if a value is an associative collection: a dict or a pkg."
|
||||
(assoc as :dict) -> true
|
||||
(assoc as :pkg) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
& ...and if two things are the same
|
||||
fn eq? {
|
||||
"Returns true if all arguments have the same value."
|
||||
|
@ -166,25 +190,10 @@ fn list? {
|
|||
}
|
||||
|
||||
fn list {
|
||||
"Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order. Associative collections return lists of (key, value) tuples."
|
||||
"Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order: sets and dicts don't have predictable or stable ordering in output. Dicts return lists of (key, value) tuples."
|
||||
(x) -> base :to_list (x)
|
||||
}
|
||||
|
||||
& TODO: make this work with Janet base
|
||||
fn set {
|
||||
"Takes an ordered collection--list or tuple--and turns it into a set."
|
||||
(xs as :list) -> base :into (${}, xs)
|
||||
(xs as :tuple) -> base :into (${}, xs)
|
||||
}
|
||||
|
||||
fn set? {
|
||||
"Returns true if a value is a set."
|
||||
(xs as :set) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
& add a contains? or has? function
|
||||
|
||||
fn fold {
|
||||
"Folds a list."
|
||||
(f as :fn, xs as :list) -> fold (f, xs, f ())
|
||||
|
@ -245,30 +254,29 @@ fn concat {
|
|||
(xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
|
||||
}
|
||||
|
||||
& the console: sending messages to the outside world
|
||||
& the console is *both* something we send to the host language's console
|
||||
& ...and also a list of messages.
|
||||
& box console = []
|
||||
|
||||
& fn flush! {
|
||||
& "Clears the console, and returns the messages."
|
||||
& () -> {
|
||||
& let msgs = unbox (console)
|
||||
& store! (console, [])
|
||||
& msgs
|
||||
& }
|
||||
& }
|
||||
fn set {
|
||||
"Takes an ordered collection--list or tuple--and turns it into a set."
|
||||
(xs as :list) -> fold (append, xs, ${})
|
||||
(xs as :tuple) -> do xs > list > set
|
||||
}
|
||||
|
||||
& fn add_msg! {
|
||||
& "Adds a message to the console."
|
||||
& (msg as :string) -> update! (console, append (_, msg))
|
||||
& (msgs as :list) -> {
|
||||
& base :print! (("adding msg", msgs))
|
||||
& let msg = do msgs > map (string, _) > join
|
||||
& base :print! (("msg: ", msg))
|
||||
& update! (console, append (_, msg))
|
||||
& }
|
||||
& }
|
||||
fn set? {
|
||||
"Returns true if a value is a set."
|
||||
(xs as :set) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn contains? {
|
||||
"Returns true if a set or list contains a value."
|
||||
(value, set as :set) -> bool (base :get (set, value))
|
||||
(value, list as :list) -> contains? (value, set (list))
|
||||
}
|
||||
|
||||
fn omit {
|
||||
"Returns a new set with the value omitted."
|
||||
(value, s as :set) -> base :disj (s, value)
|
||||
}
|
||||
|
||||
fn print! {
|
||||
"Sends a text representation of Ludus values to the console."
|
||||
|
@ -283,15 +291,6 @@ fn show {
|
|||
(x) -> base :show (x)
|
||||
}
|
||||
|
||||
fn prn! {
|
||||
"Prints the underlying Clojure data structure of a Ludus value."
|
||||
(x) -> {
|
||||
base :prn (x)
|
||||
& add_msg! (x)
|
||||
:ok
|
||||
}
|
||||
}
|
||||
|
||||
fn report! {
|
||||
"Prints a value, then returns it."
|
||||
(x) -> {
|
||||
|
@ -299,7 +298,7 @@ fn report! {
|
|||
x
|
||||
}
|
||||
(msg as :string, x) -> {
|
||||
print! (concat (msg, show (x)))
|
||||
print! (concat ("{msg} ", show (x)))
|
||||
x
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +331,7 @@ fn join {
|
|||
([]) -> ""
|
||||
([str as :string]) -> str
|
||||
(strs as :list) -> join (strs, "")
|
||||
([str], separator as :string) -> str
|
||||
([str as :string], separator as :string) -> str
|
||||
([str, ...strs], separator as :string) -> fold (
|
||||
fn (joined, to_join) -> concat (joined, separator, to_join)
|
||||
strs
|
||||
|
@ -398,13 +397,7 @@ fn sentence {
|
|||
(strs as :list) -> join (strs, " ")
|
||||
}
|
||||
|
||||
|
||||
& in another prelude, with a better actual base language than Java (thanks, Rich), counting strings would be reasonable but complex: count/bytes, count/points, count/glyphs. Java's UTF16 strings make this unweildy.
|
||||
|
||||
& TODO: add trim, trim/left, trim/right; pad/left, pad/right
|
||||
& ...also a version of at,
|
||||
|
||||
&&& references: mutable state and state changes
|
||||
&&& boxes: mutable state and state changes
|
||||
|
||||
fn box? {
|
||||
"Returns true if a value is a box."
|
||||
|
@ -650,17 +643,17 @@ fn at {
|
|||
|
||||
fn first {
|
||||
"Returns the first element of a list or tuple."
|
||||
(xs) -> at (xs, 0)
|
||||
(xs) if ordered? (xs) -> at (xs, 0)
|
||||
}
|
||||
|
||||
fn second {
|
||||
"Returns the second element of a list or tuple."
|
||||
(xs) -> at (xs, 1)
|
||||
(xs) if ordered? (xs) -> at (xs, 1)
|
||||
}
|
||||
|
||||
fn last {
|
||||
"Returns the last element of a list or tuple."
|
||||
(xs) -> at (xs, dec (count (xs)))
|
||||
(xs) if ordered? (xs) -> at (xs, dec (count (xs)))
|
||||
}
|
||||
|
||||
fn butlast {
|
||||
|
@ -731,9 +724,6 @@ fn or {
|
|||
(x, y, ...zs) -> fold (or, zs, base :or (x, y))
|
||||
}
|
||||
|
||||
&&& associative collections: dicts, structs, namespaces
|
||||
& TODO?: get_in, update_in, merge
|
||||
& TODO?: consider renaming these: put & take, not assoc/dissoc
|
||||
fn assoc {
|
||||
"Takes a dict, key, and value, and returns a new dict with the key set to value."
|
||||
() -> #{}
|
||||
|
@ -791,30 +781,6 @@ fn diff {
|
|||
}
|
||||
}
|
||||
|
||||
fn coll? {
|
||||
"Returns true if a value is a collection: dict, list, pkg, tuple, or set."
|
||||
(coll as :dict) -> true
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(coll as :set) -> true
|
||||
(coll as :pkg) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn ordered? {
|
||||
"Returns true if a value is an indexed collection: list or tuple."
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn assoc? {
|
||||
"Returns true if a value is an associative collection: a dict or a pkg."
|
||||
(assoc as :dict) -> true
|
||||
(assoc as :pkg) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
& TODO: consider merging `get` and `at`
|
||||
fn get {
|
||||
"Takes a key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value."
|
||||
|
@ -843,15 +809,11 @@ fn dict? {
|
|||
(_) -> false
|
||||
}
|
||||
|
||||
& TODO: make this less awkward once we have tail recursion
|
||||
fn each! {
|
||||
"Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil."
|
||||
(f! as :fn, []) -> nil
|
||||
(f! as :fn, [x]) -> { f! (x); nil }
|
||||
(f! as :fn, [...xs]) -> loop (xs) with {
|
||||
([x]) -> { f! (x); nil }
|
||||
([x, ...xs]) -> { f! (x); recur (xs) }
|
||||
}
|
||||
(f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) }
|
||||
}
|
||||
|
||||
&&& Trigonometry functions
|
||||
|
@ -938,18 +900,38 @@ fn atan/2 {
|
|||
}
|
||||
|
||||
fn mod {
|
||||
"Returns the modulus of num and div. Truncates towards negative infinity."
|
||||
"Returns the modulus of num and div. Truncates towards negative infinity. Panics if div is 0."
|
||||
(num as :number, 0) -> panic! "Division by zero."
|
||||
(num as :number, div as :number) -> base :mod (num, div)
|
||||
}
|
||||
|
||||
fn mod/0 {
|
||||
"Returns the modulus of num and div. Truncates towards negative infinity. Returns 0 if div is 0."
|
||||
(num as :number, 0) -> 0
|
||||
(num as :number, div as :number) -> base :mod (num, div)
|
||||
}
|
||||
|
||||
fn mod/safe {
|
||||
"Returns the modulus of num and div in a result tuple, or an error if div is 0. Truncates towards negative infinity."
|
||||
(num as :number, 0) -> (:err, "Division by zero.")
|
||||
(num as :number, div as :number) -> (:ok, base :mod (num, div))
|
||||
}
|
||||
|
||||
fn square {
|
||||
"Squares a number."
|
||||
(x as :number) -> mult (x, x)
|
||||
}
|
||||
|
||||
fn sqrt {
|
||||
"Returns the square root of a number."
|
||||
(x as :number) -> base :sqrt (x)
|
||||
"Returns the square root of a number. Panics if the number is negative."
|
||||
(x as :number) if not (neg? (x)) -> base :sqrt (x)
|
||||
}
|
||||
|
||||
fn sqrt/safe {
|
||||
"Returns a result containing the square root of a number, or an error if the number is negative."
|
||||
(x as :number) -> if not (neg? (x))
|
||||
then (:ok, base :sqrt (x))
|
||||
else (:err, "sqrt of negative number")
|
||||
}
|
||||
|
||||
fn sum_of_squares {
|
||||
|
@ -957,18 +939,21 @@ fn sum_of_squares {
|
|||
() -> 0
|
||||
(x as :number) -> square (x)
|
||||
(x as :number, y as :number) -> add (square (x), square (y))
|
||||
(x, y, ...zs) -> fold (sum_of_squares, zs, sum_of_squares (x, y))
|
||||
(x, y, ...zs) -> fold (
|
||||
fn (sum, z) -> add (sum, square (z))
|
||||
zs
|
||||
sum_of_squares (x, y))
|
||||
}
|
||||
|
||||
fn dist {
|
||||
"Returns the distance from the origin to a point described by (x, y)."
|
||||
"Returns the distance from the origin to a point described by x and y, or by the vector (x, y)."
|
||||
(x as :number, y as :number) -> sqrt (sum_of_squares (x, y))
|
||||
((x, y)) -> dist (x, y)
|
||||
}
|
||||
|
||||
&&& more number functions
|
||||
fn random {
|
||||
"Returns a random number. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a list, it returns a random member of that list."
|
||||
"Returns a random something. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a collection (list, dict, set), it returns a random member of that collection."
|
||||
() -> base :random ()
|
||||
(n as :number) -> mult (n, random ())
|
||||
(m as :number, n as :number) -> add (m, random (sub (n, m)))
|
||||
|
@ -980,6 +965,7 @@ fn random {
|
|||
let key = do d > keys > random
|
||||
get (key, d)
|
||||
}
|
||||
(s as :set) -> do s > list > random
|
||||
}
|
||||
|
||||
fn random_int {
|
||||
|
@ -1048,10 +1034,12 @@ fn unwrap_or {
|
|||
|
||||
fn assert! {
|
||||
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
|
||||
(value) -> if value then value else panic! string ("Assert failed:", value)
|
||||
(value) -> if value
|
||||
then value
|
||||
else panic! "Assert failed: {value}"
|
||||
(msg, value) -> if value
|
||||
then value
|
||||
else panic! string ("Assert failed: ", msg, " with ", value)
|
||||
else panic! "Assert failed: {msg} with {value}"
|
||||
}
|
||||
|
||||
&&& Turtle & other graphics
|
||||
|
@ -1198,8 +1186,6 @@ fn back! {
|
|||
|
||||
let bk! = back!
|
||||
|
||||
& turtles, like eveyrthing else in Ludus, use turns by default,
|
||||
& not degrees
|
||||
fn left! {
|
||||
"Rotates the turtle left, measured in turns. Alias: lt!"
|
||||
(turns as :number) -> add_command! ((:left, turns))
|
||||
|
@ -1374,6 +1360,7 @@ pkg Prelude {
|
|||
coll? & dicts lists sets tuples
|
||||
colors & turtles
|
||||
concat & string list set
|
||||
contains? & list set
|
||||
cos & math
|
||||
count & string list set tuple dict
|
||||
dec & math
|
||||
|
@ -1430,6 +1417,8 @@ pkg Prelude {
|
|||
max & math
|
||||
min & math
|
||||
mod & math
|
||||
mod/0
|
||||
mod/safe
|
||||
mult & math
|
||||
neg & math
|
||||
neg? & math
|
||||
|
@ -1439,6 +1428,7 @@ pkg Prelude {
|
|||
odd? & math
|
||||
ok & results
|
||||
ok? & results
|
||||
omit & set
|
||||
or & bool
|
||||
ordered? & lists tuples strings
|
||||
p5_calls & turtles
|
||||
|
@ -1455,7 +1445,6 @@ pkg Prelude {
|
|||
pos? & math
|
||||
position & turtles
|
||||
print! & environment
|
||||
& prn! & environment
|
||||
pu! & turtles
|
||||
pw! & turtles
|
||||
rad/deg & math
|
||||
|
@ -1480,6 +1469,8 @@ pkg Prelude {
|
|||
some & values
|
||||
some? & values
|
||||
split & strings
|
||||
sqrt & math
|
||||
sqrt/safe & math
|
||||
square & math
|
||||
state & environment
|
||||
store! & boxes
|
||||
|
|
Loading…
Reference in New Issue
Block a user