This is brev-separate
, a miscellaneous hodge-podge of macros and
procedures that all have the shared aim of brevity. Sort of my take on
the clojurian
and miscmacros
and (chicken base)
genre.
It’s called brev-separate
since the full brev
egg also imports and
reexports a bunch of other eggs, including the aforementioned
clojurian
and miscmacros
.
Chicken has syntax-rules macros and ir-macros. Let’s shave off some of the boiler plate so that it’s easier to make macros.
There is already the wonderful define-syntax-rule
in miscmacros
but here is also define-syntax-rules
:
(define-syntax-rules foo ()
((foo bar) (+ bar 3))
((foo bar baz) (* bar baz)))
It’s just the ultra lazy person’s shorthand for:
(define-syntax foo
(syntax-rules ()
((foo bar) (+ bar 3))
((foo bar baz) (* bar baz))))
define-ir-syntax*
is more interesting.
(define-ir-syntax* name
(pattern body) ...)
It uses matchable
to dispatch between different call signatures
(kinda similar to how syntax-rules
work) while also allowing you to
inject
, compare
, strip-syntax
and syntax
inside, as per usual
with ir-macros.
Here’s an example:
(define-ir-syntax*
(aif test yes no)
`(let ((,(inject 'it) ,test))
(if ,(inject 'it) ,yes ,no)))
(aif (... expensive test ...)
(car it)
(print "oh! no!"))
When you have multiple call-signatures, wrap pattern / body set with parens.
(define-ir-syntax*
((aif #f yes no) no)
((aif test yes no)
`(let ((,(inject 'it) ,test))
(if ,(inject 'it) ,yes ,no))))
Sometimes pattern matching is overkill or you have something else in mind.
define-ir-syntax
macros are just
(define-ir-syntax name body)
where the body has access to body
, inject
, compare
,
strip-syntax
and syntax
, as in the following example:
(define-ir-syntax comp-prod
(apply * body))
(comp-prod 2 3 4)
⇒ 24
As a rule of thumb, if you are deliberately injecting new names into the namespace that’s when you are using ir-macros, and when you want to avoid doing that, use syntax-rules.
(define-closure bindings head body ...)
This works like your normal
(define head body ...)
except that bindings
are lexically closed over body.
(define-closure (x 0) (counter) (inc! x))
(counter) (counter) (counter)
⇒ 1 2 3
The pairs of bindings aren’t individual paren-wrapped, just alternating between name and expression. The set of bindings as a whole has parens.
(define-closure (x 0 y 10) (jolly) (list (inc! x) (dec! y)))
(jolly) (jolly) (jolly)
⇒ (1 9) (2 8) (3 7)
Deprecated♥
Calling match-define
directly is deprecated in favor
of using the macro from match-generics, which can
expand to it or to normal define
as needed. (But match-define
is
still used internally to avoid circular dependencies.)
call-table
, call-table*
, call-vector
, call-string
, call-list
, and call-record
A closure interface to hash-tables.
(define arity (call-table))
(define color (call-table))
(arity cons 2)
(color cons 'blue)
((over (x cons)) (list arity color))
⇒ (2 blue)
call-table
takes two optional keyword argument, default:
, to set
the default response for unknown keys, and seed:
which can be a
hash-table or an alist, and defaults to empty.
There is also call-table*
which by default cons its values to a list
instead of replacing them. It takes four keyword arguments. proc:
which defaults to cons
, initial which defaults to '()
, and unary
which defaults to #f
, and seed:
as above.
Both versions of call-table lets you access the underlying hash-table
by calling them with no arguments, and to set them by calling them
with the keyword argument update:
.
(color update: my-other-hash-table)
Full documentation for call-tables Full documentation for callable arrays
Sometimes you think call-table
is convenient but you only need one key.
For call-key
, just use make-parameter
.
But call-key*
is awesome since it accumulates its values.
It has the same proc
, unary
, and initial
keyword arguments as
call-table*
. It doesn’t have seed
because the idea is that you
just use inititial
. The generated procedure has update
(which
takes a new list as argument) and get
(which you only need for
unary
call-keys).
(define horses (call-key*))
(horses 'ruby)
(horses 'kind-girl)
(horses 'tornado)
(horses)
⇒ (tornado kind-girl ruby)
This is sugar for creating call-tables with some values already filled.
The q variants are implicitly quasiquoted while the non-q variants aren’t.
I.e.
(let ((banana-color 'yellow))
(ctq banana ,banana-color apple red))
is equivalent to
(let ((banana-color 'yellow))
(call-table seed: `((banana . ,banana-color) (apple . red))))
and
(let ((banana-color 'yellow))
(ct 'banana banana-color 'apple 'red))
The * variants create call-table*
instances instead, and splice one level of lists:
(ctq* banana yellow apple (green red))
These call-tables aren’t closed, you can add more keys and values to them.
This is for making functions that implicitly returns ‘() on an
empty?
first argument. In other words, it defines a body for
patterns with some non-empty value as first argument, hence the
name define-some
.
For example,
(define-some (descseq num)
(cons num (descseq (sub1 num))))
is shorthand for
(define (descseq num)
(if (empty? num)
'()
(cons num (descseq (sub1 num)))))
so
(descseq 5)
⇒ (5 4 3 2 1)
(define-parameters foo 0 bar #t baz '() quux 'banana)
is shorthand for
(define foo (make-parameter 0))
(define bar (make-parameter #t))
(define baz (make-parameter '()))
(define quux (make-parameter 'banana))
It’s nice that you can make specific curries with the SRFI-219 style define heads (which is implemented per default in Chicken).
That’s nice if you know exactly how many stragglers and how many immediate args you have, but sometimes you need the currying itself to be arbitrary arity.
Let’s say you already have something like:
(define (foo bar baz bax)
(print baz)
(+ bar baz bax))
but you realize you need arbitrary-arity currying.
Just change it to use define-curry
instead of define
:
(define-curry (foo bar baz bax)
(print baz)
(+ bar baz bax))
(=
(foo 100 20 3)
((foo 100) 20 3)
((foo 100 20) 3)
((foo) 100 20 3)
(((foo) 100) 20 3)
(((foo 100) 20) 3)
((((foo) 100) 20) 3))
Prints seven 20 and returns #t
.
It only works when foo
otherwise would have fixed arity.
c
a.k.a. 🍛
a.k.a. @>
This isn’t the traditional c-combinator from mockingbirds and such. It’s just a one-letter spelling of “curry”. It’s a function combinator.
((c + 1 20 300) 4000 50000)
⇒ 54321
I also exported it using the name 🍛 for those who find emoji names more comfortable to use due to namespace issues.
I later found out that @>
from the holes
egg is same the
combinator as this. Then, a few months later than that, I found out
that partial
from Clojure is the same combinator as this.
It has arbitrary arity and can work on arbitrary arity functions, but isn’t recursive to multiple levels.
(fn body ...)
is shorthand for
(lambda some-basic-bindings body ...)
where some-basic-bindings is one of
and the fn macro automatically figures out which of those four you mean.
The function can refer to itself as f
.
(In other words, if the body refers to f
, the expansion gets wrapped in (letrec ((f …)) f)
.)
(over body ...)
is shorthand for
(c map (lambda some-basic-bindings body ...))
except that the map can take any number of lists (or other sequences,
like strings) and that i
is also anaphorically bound to the list
index in body
.
Here is an example:
((over (+ x x y i))
'(10 20 40) '(3 6 9))
⇒ (23 47 91)
Like fn
, the function can refer to itself as f
.
((over (display x) (eif x (f (sub1 x)) x)) '(2 4 3))
displays 210432103210 and returns (0 0 0)
.
Here is a functional combinator for Scheme that lets its arguments treat their arguments as if they were lists.
((as-list (c filter odd?)) 130752)
⇒ 1375
((as-list cdr reverse) 23311358)
⇒ 5311332
((as-list delete-duplicates) 23311358)
⇒ 23158
(define (vowel? l) ((as-list (c member l)) "aeiou"))
((as-list (c filter vowel?)) "magnetic mountaintop")
⇒ “aeiouaio”
Together with over
:
((over (if (vowel? x) x (char-upcase x))) "fleet foxes")
⇒ “FLeeT FoXeS”
Sinilar to as-list
but for when you wanna use string procedures on
lists of characters, or on symbols.
Similar to what’s in other libraries, brev-separate ships a memoize, to cache function results for args. (memoize! proc) is a macro that expands to (define proc (memoize proc)), which is good for recursive functions.
Sometimes you just need an arbitrarily long tree dereferencer.
(make-tree-accessor cddadadaddddar)
makes cddadadaddddar
real. Works for any sequence of a’s and d’s.
As above, but uses scar and scdr instead of car and cdr.
newline1
is a version of Scheme’s newline
that does nothing the
first time it’s called.
(for-each (fn (newline1) (display x)) '(a b c d e))
prints
a
b
c
d
e
skip1
is a general combinator that makes these:
(define tab1 (skip1 (fn (newline) (display " "))))
(for-each (fn (tab1) (display x)) '(ol li li li))
prints
ol
li
li
li
To reset them (so they’ll skip their next invocation), call them with a single argument, 'reset
.
(newline1 'reset)
(tab1 'reset)
skip1
can take any number of procedures with any number of arguments, it runs ((compose proc1 proc2 …) arg1 arg2 …)
.
For example,
(define conv1 (skip1 print add1 *))
(for-each conv1 '(1 2 3) '(10 20 30))
prints
41
91
This is something that is sometimes cozy:
(with-result
(print 1 2 (save 3) 4 5 6)
(print 7 8))
Prints 123456 and 78, returns 3
(aif-with-result (odd? (save 3)) (+ it 4) (+ it 1000))
⇒ 7
That tests the entire expression but only stores the saved part into it
.
Combining aif
and with-result
would do the opposite:
(aif
(with-result (pred? (save 27)))
it #f)
That tests 27 and stores 27 into it
, while the pred? call is thrown away.
(keep proc args…)
runs proc for its sideeffects and returns the last arg.
For example, (+ 3 (keep print 'hi 4))
prints hi4 and returns 7.
It is curried on the procedure so you can ((keep proc) args…)
.
A shorthand for with-result
when you only have one expression and only care about the last (or only) arg.
This is a generic predicate to see if a string is “”, a list is ‘(), a number is 0 etc.
eif
, econd
, ewhen
, eor
and eand
eif
is a version of if
(or, to be precise, of aif
since it is
anaphoric) that treats empty things as falsy.
(eif "" it 'no)
⇒ no
econd
, similarly, is an “empty is falsy” version of acond
. There’s
also ewhen
, eor
and eand
.
(eor 0 (add1 it))
⇒ 1
like?
is a unary curried version of equal?
is?
is a unary curried version of eq?
scdr
, and scar
A scar is like a car but returns ‘() if the pair has no car. A scdr is like a cdr but returns ‘() if the pair has no cdr.
(normalize-absolute-pathname filename)
Uses current-directory to try to figure out an absolute path for filename.
Here is an generic slice multimethod for Scheme.
(slice '(hello now there you are) 1 3)
⇒ (now there)
(slice "so this is where you are hiding" 3 7)
⇒ (#\t #\h #\i #\s)
(let ((str "so this is where you are hiding"))
(set! (slice str 3 7) "that")
str)
⇒ “so that is where you are hiding”
(slice 1243153 -3 -0)
⇒ (1 5 3)
Because of Scheme’s call-by-value semantics, set!
doesn’t work on
numbers.♥
Descend is sort of like a named let
except it does three magic things.
First of all, the let tag is always desc
. I almost named the macro
itself desc
but that would’ve been bad since then you couldn’t nest
them.
Second of all, if the binding is to a value of the same name e.g.
(lis lis)
you can just put the name there. You can mix these
shorthand bindings with normal bindings.
Now, if the first binding starts of as empty?
, the third magic thing
(which I’ll get into shortly) is disabled and you can go on your merry
way only using the above two magics.
(descend ((sum 0) (nums '(1 2 3 4)))
(if (null? nums) sum
(desc (+ sum (car nums)) (cdr nums))))
⇒ 10
Otherwise, if it does start out non-empty…
(descend ((nums '(1 2 3 4)))
(+ (car nums) (desc (cdr nums))))
⇒ 10
That’s right. It only recurs the value is non-empty.
?->
is a combinator that takes a predicate test and a transformer, and returns a unary procedure that transforms its argument if the predicate applies.
That’s a mouthful. Let’s break it down with some examples:prcx
You can make a car
that only cars if the list is not null:
(map (?-> (o not null?) car) '((a b c) () (1 2 3) () ()))
⇒ (a () 1 () ())
Or a reverse that only reverses if it receives a list:
(map (?-> list? reverse) '(a (l u f r e d n o w) "summer's" (y a d)))
⇒ (a (w o n d e r f u l) "summer's" (d a y))
The default when the predicate doesn’t apply is to just pass through the argument, but you can set a different default with a keyword argument:
(map (?-> (o not null?) car #:default #f) '((a b c) () (1 2 3) () ()))
⇒ (a #f 1 #f #f)
Sort of like a one-branch aif
or an always-true awhen
.
(with (+ 13 14) (print it) (+ it it))
Prints 27
and returns 54.
There’s also this
that uses the word that
as the anaphor instead,
since the anaphor it
is so often already taken in anaphoric
programming.
Shorthand for (fn (with-input-from-string x read))
.
Like string->read
, but leave it as a string if it wouldn’t make sense. (For example, contains spaces, periods, or parantheses.)
Ref* is a universal version of list-ref, hash-table-ref etc.
It can handle lists, alists, hash-tables, strings, vectors, records, and all callable procedures (like call-tables).
You can also pass multiple arguments to deref*erence recursively. Designed by Chris Brannon.
The order is left to right, like Java’s dots or Clojure’s arrow, unlike compose or nested calls. That is to say, the left-most is the outermost.
Here’s an extended example.
(define foo (make-hash-table))
(set! (hash-table-ref* foo 'bar) '((x . #(1 2 3)) (y . #(4 5 6))))
(ref* foo 'bar)
⇒ ((x . #(1 2 3)) (y . #(4 5 6)))
(ref* foo 'bar 'y)
⇒ #(4 5 6)
(ref* foo 'bar 'y 0)
⇒ 4
There’s also the refx
partially applied variant: it takes only the indices and returns a procedure of one argument.
For example, you could make a car
that worked on strings and vectors too, like this: (refx 0)
and you could make a caddar
like this: (refx 2 0)
.
(for-each-line filename body ...)
body
is called for its sideeffects once per line of text in
filename
with the variable line
anaphorically bound to that line.
(for-each-stdin body ...)
body
is called for its sideeffects once per line of text in standard
input (a.k.a. (current-input-port)
) with the variable line
anaphorically bound to that line.
For example:
(for-each-line "/tmp/foo" (print line))
(niy)
NIY stands for “not implemented yet”.
Errors out if called with no arguments or if any of its arguments are true. Sort of like a living FIXME.
(die "Write your message here")
Like print
, but writes to error port and calls (exit 1).
git clone https://idiomdrottning.org/brev-separate