You are looking at historical revision 40100 of this page. It may differ significantly from its current revision.

brev-separate

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.

Making macros

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.

define-syntax-rules

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*

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
  ((aif test yes no)
   `(let ((,(inject 'it) ,test))
      (if ,(inject 'it) ,yes ,no))))

(aif (... expensive test ...)
     (car it)
     (print "oh! no!"))

define-ir-syntax

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 exp, inject, compare, strip-syntax and syntax.

exp is the macro call as a whole, as in the following example:

(define-ir-syntax comp-prod
  (apply * (cdr exp)))

(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.

Making procedures

define-closure

(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)

match-define

matchable has match-lambda and match-lambda* as shortcuts, and they’re fantastic, but let’s also add a match-define as a shortcut on top of them.

How about these sorta Haskell-style semantics?

(match-define
 (pat body ...)
 ...)

Her's an example:

(match-define
   ((my-map proc ()) '())
   ((my-map proc (x . xs))
    (cons (proc x) (my-map proc xs))))

(my-map - (iota 4))

β‡’ (0 -1 -2 -3)

Works with curried define too, of course:

(match-define
   (((my-map proc) ()) '())
   (((my-map proc) (x . xs))
    (cons (proc x) ((my-map proc) xs))))

((my-map -) (iota 4))

β‡’ (0 -1 -2 -3)

(map (my-map -) '((1 2 3) (10 20 30) (100 200 300)))

β‡’ ((-1 -2 -3) (-10 -20 -30) (-100 -200 -300))

(Limitation: This implementation only destructures the outer argument list. For future versions it would be awesome to be able to pattern match on every level.)

match-define-closure

Combining match-define and define-closure into one glorious Voltron!

(match-define-closure bindings (pat body) ...)

Here's an example:

(match-define-closure
 (x 0)
 ((counter) (inc! x))
 ((counter 'reset) (set! x 0)))

(counter) (counter) (counter 'reset) (counter) (counter)

β‡’ 1 2 1 2

Here is another example, the arity-table from citeSoftware Design for Flexibility.

(match-define-closure
 (ht (make-hash-table))
 ((arity proc) (hash-table-ref ht proc))
 ((arity proc a) (hash-table-set! ht proc a)))

(arity cons 2)
(arity cons)

β‡’ 2

call-table

The previous construct is so generally useful so let's just provide it as call-table.

(define arity (call-table))
(define color (call-table))

(arity cons 2)
(color cons 'blue)

(map (cut <> cons) (list arity color))

β‡’ (2 blue)

define-some

This is for making functions that implicitly returns '() on an empty? first argument. In other words, it define 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-curry

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.

It has arbitrary arity and can work on arbitrary arity functions, but isn't recursive to multiple levels.

over

(over bindings body ...)

is shorthand for

(cut map (lambda bindings body ...) <>)

except that the map can take any number of lists (as many as there are bindings in bindings).

Here is an example:

((over (x y) (+ x x y))
 '(10 20 40) '(3 6 9))

β‡’ (23 46 89)

as-list

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:

((as-list (over (l) (if (vowel? l) l (char-upcase l)))) "fleet foxes")

β‡’ β€œFLeeT FoXeS”

Making values

with-result

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

empty?

This is a generic predicate to see if a string is "", a list is '(), a number is 0 etc.

eif and econd

eif is a version of if (or, to be precise, of aif since it anaphoric) that treats empty things as falsy.

(eif "" it 'no)

β‡’ no

econd, similarly, is an "empty is falsy" version of acond.

Author

Idiomdrottning

Source code

git clone https://idiomdrottning.org/brev-separate

License

Β© 2021 Idiomdrottning.

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer:

This software is provided by Idiomdrottning "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall Idiomdrottning be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.