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
|
& some forward declarations
|
||||||
& TODO: fix this so that we don't need (as many of) them
|
& 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 and
|
||||||
|
fn append
|
||||||
fn apply_command
|
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 state/call
|
||||||
|
fn store!
|
||||||
|
fn string
|
||||||
|
fn turn/rad
|
||||||
|
fn unbox
|
||||||
|
fn update!
|
||||||
|
|
||||||
& the very base: know something's type
|
& the very base: know something's type
|
||||||
fn type {
|
fn type {
|
||||||
|
@ -36,6 +36,30 @@ fn type {
|
||||||
(x) -> base :type (x)
|
(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
|
& ...and if two things are the same
|
||||||
fn eq? {
|
fn eq? {
|
||||||
"Returns true if all arguments have the same value."
|
"Returns true if all arguments have the same value."
|
||||||
|
@ -166,25 +190,10 @@ fn list? {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
(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 {
|
fn fold {
|
||||||
"Folds a list."
|
"Folds a list."
|
||||||
(f as :fn, xs as :list) -> fold (f, xs, f ())
|
(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))
|
(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! {
|
fn set {
|
||||||
& "Clears the console, and returns the messages."
|
"Takes an ordered collection--list or tuple--and turns it into a set."
|
||||||
& () -> {
|
(xs as :list) -> fold (append, xs, ${})
|
||||||
& let msgs = unbox (console)
|
(xs as :tuple) -> do xs > list > set
|
||||||
& store! (console, [])
|
}
|
||||||
& msgs
|
|
||||||
& }
|
|
||||||
& }
|
|
||||||
|
|
||||||
& fn add_msg! {
|
fn set? {
|
||||||
& "Adds a message to the console."
|
"Returns true if a value is a set."
|
||||||
& (msg as :string) -> update! (console, append (_, msg))
|
(xs as :set) -> true
|
||||||
& (msgs as :list) -> {
|
(_) -> false
|
||||||
& base :print! (("adding msg", msgs))
|
}
|
||||||
& let msg = do msgs > map (string, _) > join
|
|
||||||
& base :print! (("msg: ", msg))
|
fn contains? {
|
||||||
& update! (console, append (_, msg))
|
"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! {
|
fn print! {
|
||||||
"Sends a text representation of Ludus values to the console."
|
"Sends a text representation of Ludus values to the console."
|
||||||
|
@ -283,15 +291,6 @@ fn show {
|
||||||
(x) -> base :show (x)
|
(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! {
|
fn report! {
|
||||||
"Prints a value, then returns it."
|
"Prints a value, then returns it."
|
||||||
(x) -> {
|
(x) -> {
|
||||||
|
@ -299,7 +298,7 @@ fn report! {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
(msg as :string, x) -> {
|
(msg as :string, x) -> {
|
||||||
print! (concat (msg, show (x)))
|
print! (concat ("{msg} ", show (x)))
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -332,7 +331,7 @@ fn join {
|
||||||
([]) -> ""
|
([]) -> ""
|
||||||
([str as :string]) -> str
|
([str as :string]) -> str
|
||||||
(strs as :list) -> join (strs, "")
|
(strs as :list) -> join (strs, "")
|
||||||
([str], separator as :string) -> str
|
([str as :string], separator as :string) -> str
|
||||||
([str, ...strs], separator as :string) -> fold (
|
([str, ...strs], separator as :string) -> fold (
|
||||||
fn (joined, to_join) -> concat (joined, separator, to_join)
|
fn (joined, to_join) -> concat (joined, separator, to_join)
|
||||||
strs
|
strs
|
||||||
|
@ -398,13 +397,7 @@ fn sentence {
|
||||||
(strs as :list) -> join (strs, " ")
|
(strs as :list) -> join (strs, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&&& boxes: mutable state and state changes
|
||||||
& 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
|
|
||||||
|
|
||||||
fn box? {
|
fn box? {
|
||||||
"Returns true if a value is a box."
|
"Returns true if a value is a box."
|
||||||
|
@ -650,17 +643,17 @@ fn at {
|
||||||
|
|
||||||
fn first {
|
fn first {
|
||||||
"Returns the first element of a list or tuple."
|
"Returns the first element of a list or tuple."
|
||||||
(xs) -> at (xs, 0)
|
(xs) if ordered? (xs) -> at (xs, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn second {
|
fn second {
|
||||||
"Returns the second element of a list or tuple."
|
"Returns the second element of a list or tuple."
|
||||||
(xs) -> at (xs, 1)
|
(xs) if ordered? (xs) -> at (xs, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last {
|
fn last {
|
||||||
"Returns the last element of a list or tuple."
|
"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 {
|
fn butlast {
|
||||||
|
@ -731,9 +724,6 @@ fn or {
|
||||||
(x, y, ...zs) -> fold (or, zs, base :or (x, y))
|
(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 {
|
fn assoc {
|
||||||
"Takes a dict, key, and value, and returns a new dict with the key set to value."
|
"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`
|
& TODO: consider merging `get` and `at`
|
||||||
fn get {
|
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."
|
"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
|
(_) -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
& TODO: make this less awkward once we have tail recursion
|
|
||||||
fn each! {
|
fn each! {
|
||||||
"Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil."
|
"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, []) -> nil
|
||||||
(f! as :fn, [x]) -> { f! (x); nil }
|
(f! as :fn, [x]) -> { f! (x); nil }
|
||||||
(f! as :fn, [...xs]) -> loop (xs) with {
|
(f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) }
|
||||||
([x]) -> { f! (x); nil }
|
|
||||||
([x, ...xs]) -> { f! (x); recur (xs) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&&& Trigonometry functions
|
&&& Trigonometry functions
|
||||||
|
@ -938,18 +900,38 @@ fn atan/2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mod {
|
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)
|
(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 {
|
fn square {
|
||||||
"Squares a number."
|
"Squares a number."
|
||||||
(x as :number) -> mult (x, x)
|
(x as :number) -> mult (x, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sqrt {
|
fn sqrt {
|
||||||
"Returns the square root of a number."
|
"Returns the square root of a number. Panics if the number is negative."
|
||||||
(x as :number) -> base :sqrt (x)
|
(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 {
|
fn sum_of_squares {
|
||||||
|
@ -957,18 +939,21 @@ fn sum_of_squares {
|
||||||
() -> 0
|
() -> 0
|
||||||
(x as :number) -> square (x)
|
(x as :number) -> square (x)
|
||||||
(x as :number, y as :number) -> add (square (x), square (y))
|
(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 {
|
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 as :number, y as :number) -> sqrt (sum_of_squares (x, y))
|
||||||
((x, y)) -> dist (x, y)
|
((x, y)) -> dist (x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
&&& more number functions
|
&&& more number functions
|
||||||
fn random {
|
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 ()
|
() -> base :random ()
|
||||||
(n as :number) -> mult (n, random ())
|
(n as :number) -> mult (n, random ())
|
||||||
(m as :number, n as :number) -> add (m, random (sub (n, m)))
|
(m as :number, n as :number) -> add (m, random (sub (n, m)))
|
||||||
|
@ -980,6 +965,7 @@ fn random {
|
||||||
let key = do d > keys > random
|
let key = do d > keys > random
|
||||||
get (key, d)
|
get (key, d)
|
||||||
}
|
}
|
||||||
|
(s as :set) -> do s > list > random
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_int {
|
fn random_int {
|
||||||
|
@ -1048,10 +1034,12 @@ fn unwrap_or {
|
||||||
|
|
||||||
fn assert! {
|
fn assert! {
|
||||||
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
|
"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
|
(msg, value) -> if value
|
||||||
then value
|
then value
|
||||||
else panic! string ("Assert failed: ", msg, " with ", value)
|
else panic! "Assert failed: {msg} with {value}"
|
||||||
}
|
}
|
||||||
|
|
||||||
&&& Turtle & other graphics
|
&&& Turtle & other graphics
|
||||||
|
@ -1198,8 +1186,6 @@ fn back! {
|
||||||
|
|
||||||
let bk! = back!
|
let bk! = back!
|
||||||
|
|
||||||
& turtles, like eveyrthing else in Ludus, use turns by default,
|
|
||||||
& not degrees
|
|
||||||
fn left! {
|
fn left! {
|
||||||
"Rotates the turtle left, measured in turns. Alias: lt!"
|
"Rotates the turtle left, measured in turns. Alias: lt!"
|
||||||
(turns as :number) -> add_command! ((:left, turns))
|
(turns as :number) -> add_command! ((:left, turns))
|
||||||
|
@ -1374,6 +1360,7 @@ pkg Prelude {
|
||||||
coll? & dicts lists sets tuples
|
coll? & dicts lists sets tuples
|
||||||
colors & turtles
|
colors & turtles
|
||||||
concat & string list set
|
concat & string list set
|
||||||
|
contains? & list set
|
||||||
cos & math
|
cos & math
|
||||||
count & string list set tuple dict
|
count & string list set tuple dict
|
||||||
dec & math
|
dec & math
|
||||||
|
@ -1430,6 +1417,8 @@ pkg Prelude {
|
||||||
max & math
|
max & math
|
||||||
min & math
|
min & math
|
||||||
mod & math
|
mod & math
|
||||||
|
mod/0
|
||||||
|
mod/safe
|
||||||
mult & math
|
mult & math
|
||||||
neg & math
|
neg & math
|
||||||
neg? & math
|
neg? & math
|
||||||
|
@ -1439,6 +1428,7 @@ pkg Prelude {
|
||||||
odd? & math
|
odd? & math
|
||||||
ok & results
|
ok & results
|
||||||
ok? & results
|
ok? & results
|
||||||
|
omit & set
|
||||||
or & bool
|
or & bool
|
||||||
ordered? & lists tuples strings
|
ordered? & lists tuples strings
|
||||||
p5_calls & turtles
|
p5_calls & turtles
|
||||||
|
@ -1455,7 +1445,6 @@ pkg Prelude {
|
||||||
pos? & math
|
pos? & math
|
||||||
position & turtles
|
position & turtles
|
||||||
print! & environment
|
print! & environment
|
||||||
& prn! & environment
|
|
||||||
pu! & turtles
|
pu! & turtles
|
||||||
pw! & turtles
|
pw! & turtles
|
||||||
rad/deg & math
|
rad/deg & math
|
||||||
|
@ -1480,6 +1469,8 @@ pkg Prelude {
|
||||||
some & values
|
some & values
|
||||||
some? & values
|
some? & values
|
||||||
split & strings
|
split & strings
|
||||||
|
sqrt & math
|
||||||
|
sqrt/safe & math
|
||||||
square & math
|
square & math
|
||||||
state & environment
|
state & environment
|
||||||
store! & boxes
|
store! & boxes
|
||||||
|
|
Loading…
Reference in New Issue
Block a user