source: project/wiki/eggref/5/brev-separate @ 40137

Last change on this file since 40137 was 40137, checked in by Idiomdrottning, 2 months ago
File size: 10.1 KB
Line 
1== brev-separate
2[[toc:]]
3This is {{brev-separate}}, a miscellaneous hodge-podge of macros and
4procedures that all have the shared aim of brevity. Sort of my take on
5the {{clojurian}} and {{miscmacros}} and {{(chicken base)}} genre.
6
7It's called {{brev-separate}} since the full {{brev}} egg also imports
8and reexports a bunch of other eggs, including the aforementioned
9{{clojurian}} and {{miscmacros}}.
10
11
12=== Making macros
13Chicken has syntax-rules macros and ir-macros.
14Let's shave off some of the boiler plate so that it's easier to make macros.
15
16
17==== define-syntax-rules
18There is already the wonderful {{define-syntax-rule}} in {{miscmacros}}
19but here is also {{define-syntax-rules}}:
20
21 (define-syntax-rules foo ()
22   ((foo bar) (+ bar 3))
23   ((foo bar baz) (* bar baz)))
24
25It's just the ultra lazy person's shorthand for:
26
27 (define-syntax foo
28   (syntax-rules ()
29     ((foo bar) (+ bar 3))
30     ((foo bar baz) (* bar baz))))
31
32
33==== define-ir-syntax*
34{{define-ir-syntax*}} is more interesting.
35
36 (define-ir-syntax* name
37   (pattern body) ...)
38
39It 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.
40
41Here's an example:
42
43 (define-ir-syntax*
44   (aif test yes no)
45   `(let ((,(inject 'it) ,test))
46      (if ,(inject 'it) ,yes ,no)))
47 
48 (aif (... expensive test ...)
49      (car it)
50      (print "oh! no!"))
51
52When you have multiple call-signatures, wrap pattern / body set with parens.
53
54 (define-ir-syntax*
55   ((aif #f yes no) no)
56   ((aif test yes no)
57    `(let ((,(inject 'it) ,test))
58       (if ,(inject 'it) ,yes ,no))))
59
60
61==== define-ir-syntax
62Sometimes pattern matching is overkill or you have something else in mind.
63
64{{define-ir-syntax}} macros are just
65
66 (define-ir-syntax name body)
67
68where the body has access to {{body}}, {{inject}}, {{compare}},
69{{strip-syntax}} and {{syntax}}, as in the following example:
70
71 (define-ir-syntax comp-prod
72   (apply * body))
73 
74 (comp-prod 2 3 4)
75
76⇒ 24
77
78As 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.
79
80
81=== Making procedures
82
83==== define-closure
84 (define-closure bindings head body ...)
85
86This works like your normal
87
88 (define head body ...)
89
90except that {{bindings}} are lexically closed over body.
91
92 (define-closure (x 0) (counter) (inc! x))
93 
94 (counter) (counter) (counter)
95
96⇒ 1 2 3
97
98The pairs of bindings aren't individual paren-wrapped, just alternating between name and expression. The set of bindings as a whole has parens.
99
100 (define-closure (x 0 y 10) (jolly) (list (inc! x) (dec! y)))
101 
102 (jolly) (jolly) (jolly)
103
104⇒ (1 9) (2 8) (3 7)
105
106
107==== match-define
108{{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.
109
110How about these sorta Haskell-style semantics?
111
112 (match-define
113  (pat body ...)
114  ...)
115
116Her's an example:
117
118 (match-define
119    ((my-map proc ()) '())
120    ((my-map proc (x . xs))
121     (cons (proc x) (my-map proc xs))))
122 
123 (my-map - (iota 4))
124
125⇒ (0 -1 -2 -3)
126
127Works with curried define too, of course:
128
129 (match-define
130    (((my-map proc) ()) '())
131    (((my-map proc) (x . xs))
132     (cons (proc x) ((my-map proc) xs))))
133 
134 ((my-map -) (iota 4))
135
136⇒ (0 -1 -2 -3)
137
138 (map (my-map -) '((1 2 3) (10 20 30) (100 200 300)))
139
140⇒ ((-1 -2 -3) (-10 -20 -30) (-100 -200 -300))
141
142(Limitation: This implementation only destructures the outer argument
143list. For future versions it would be awesome to be able to pattern
144match on every level.)
145
146
147==== match-define-closure
148Combining {{match-define}} and {{define-closure}} into one glorious Voltron!
149
150 (match-define-closure bindings (pat body) ...)
151
152Here's an example:
153
154 (match-define-closure
155  (x 0)
156  ((counter) (inc! x))
157  ((counter 'reset) (set! x 0)))
158 
159 (counter) (counter) (counter 'reset) (counter) (counter)
160
161⇒ 1 2 1 2
162
163Here is another example, the arity-table from Software Design for Flexibility.
164
165 (match-define-closure
166  (ht (make-hash-table))
167  ((arity proc) (hash-table-ref ht proc))
168  ((arity proc a) (hash-table-set! ht proc a)))
169 
170 (arity cons 2)
171 (arity cons)
172
173⇒ 2
174
175
176==== {{call-table}}, {{call-table*}}, {{call-vector}}, {{call-string}} and {{call-list}}
177The previous construct is generally useful so let's just provide it as {{call-table}}.
178
179 (define arity (call-table))
180 (define color (call-table))
181 
182 (arity cons 2)
183 (color cons 'blue)
184 
185 (map (cut <> cons) (list arity color))
186
187⇒ (2 blue)
188
189{{call-table}} takes two optional keyword argument, {{default:}}, to set
190the default response for unknown keys, and {{seed:}} which can be a
191hash-table or an alist, and defaults to empty.
192
193There is also {{call-table*}} which by default cons its values to a list
194instead of replacing them. It takes four keyword arguments. {{proc:}}
195which defaults to {{cons}}, initial which defaults to {{'()}}, and unary
196which defaults to {{#f}}, and {{seed:}} as above.
197
198Both versions of call-table lets you access the underlying hash-table
199by calling them with no arguments, and to set them by calling them
200with the keyword argument {{update:}}.
201
202 (color update: my-other-hash-table)
203
204[[https://idiomdrottning.org/call-table|Full documentation for call-tables]]
205[[https://idiomdrottning.org/call-vector|Full documentation for callable arrays]]
206
207
208==== define-some
209This 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}}.
210
211For example,
212
213 (define-some (descseq num)
214    (cons num (descseq (sub1 num))))
215
216is shorthand for
217
218 (define (descseq num)
219   (if (empty? num)
220       '()
221       (cons num (descseq (sub1 num)))))
222
223so
224
225 (descseq 5)
226
227⇒ (5 4 3 2 1)
228
229
230==== define-curry
231It's nice that you can make specific curries with the SRFI-219 style
232define heads (which is implemented per default in Chicken).
233
234That's nice if you know exactly how many stragglers and how many
235immediate args you have, but sometimes you need the currying itself to
236be arbitrary arity.
237
238Let's say you already have something like:
239
240 (define (foo bar baz bax)
241   (print baz)
242   (+ bar baz bax))
243
244but you realize you need arbitrary-arity currying.
245
246Just change it to use {{define-curry}} instead of {{define}}:
247
248 (define-curry (foo bar baz bax)
249   (print baz)
250   (+ bar baz bax))
251 
252 (=
253  (foo 100 20 3)
254  ((foo 100) 20 3)
255  ((foo 100 20) 3)
256  ((foo) 100 20 3)
257  (((foo) 100) 20 3)
258  (((foo 100) 20) 3)
259  ((((foo) 100) 20) 3))
260
261Prints seven 20 and returns {{#t}}.
262
263It only works when {{foo}} otherwise would have fixed arity.
264
265
266==== {{c}} a.k.a. 🍛 a.k.a. {{@>}}
267This isn't the traditional c-combinator from mockingbirds and such.
268It's just a one-letter spelling of "curry". It's a function
269combinator.
270
271 ((c + 1 20 300) 4000 50000)
272
273⇒ 54321
274
275I also exported it using the name 🍛 for those who find emoji names
276more comfortable to use due to namespace issues.
277
278I later found out that {{@>}} from the {{holes}} egg is same the
279combinator as this.
280
281It has arbitrary arity and can work on arbitrary arity functions, but
282isn't recursive to multiple levels.
283
284
285==== over
286 (over bindings body ...)
287
288is shorthand for
289
290 (cut map (lambda bindings body ...) <>)
291
292except that the map can take any number of lists (as many as there are bindings in {{bindings}}).
293
294Here is an example:
295
296 ((over (x y) (+ x x y))
297  '(10 20 40) '(3 6 9))
298
299⇒ (23 46 89)
300
301
302==== as-list
303Here is a functional combinator for Scheme that lets its arguments treat their arguments as if they were lists.
304
305 ((as-list (c filter odd?)) 130752)
306
307⇒ 1375
308
309 ((as-list cdr reverse) 23311358)
310
311⇒ 5311332
312
313 ((as-list delete-duplicates) 23311358)
314
315⇒ 23158
316
317 (define (vowel? l) ((as-list (c member l)) "aeiou"))
318 ((as-list (c filter vowel?)) "magnetic mountaintop")
319
320⇒ “aeiouaio”
321
322Together with {{over}}:
323
324 ((as-list (over (l) (if (vowel? l) l (char-upcase l)))) "fleet foxes")
325
326⇒ “FLeeT FoXeS”
327
328
329==== c_r
330Sometimes you just need an arbitrarily long tree dereferencer.
331
332 (c_r cddadadaddddar)
333
334makes {{cddadadaddddar}} real. Works for any sequence of a's and d's.
335
336
337=== Making values
338
339==== with-result
340This is something that is sometimes cozy:
341
342 (with-result
343  (print 1 2 (save 3) 4 5 6)
344  (print 7 8))
345
346Prints 123456 and 78, returns 3
347
348
349==== empty?
350This is a generic predicate to see if a string is "", a list is '(), a
351number is 0 etc.
352
353
354==== {{eif}} and {{econd}}
355{{eif}} is a version of {{if}} (or, to be precise, of {{aif}} since it anaphoric) that treats empty things as falsy.
356
357 (eif "" it 'no)
358
359⇒ no
360
361{{econd}}, similarly, is an "empty is falsy" version of {{acond}}.
362
363
364==== like?
365{{like?}} is a unary curried version of {{equal?}}
366
367
368==== is?
369{{is?}} is a unary curried version of {{eq?}}
370
371
372=== Doing stuff
373
374==== for-each-line
375 (for-each-line filename body ...)
376
377{{body}} is called for its sideeffects once per line of text in
378{{filename}} with the variable {{line}} anaphorically bound to that line.
379
380For example:
381
382 (for-each-line "/tmp/foo" (print line))
383
384
385== Author
386
387[[https://idiomdrottning.org/about|Idiomdrottning]]
388
389== Source code
390
391 git clone https://idiomdrottning.org/brev-separate
392
393== License
394
395© 2021 Idiomdrottning.
396
397All rights reserved.
398
399Redistribution and use in source and binary forms, with or without
400modification, are permitted provided that the following conditions are
401met:
402
4031. Redistributions of source code must retain the above copyright
404notice, this list of conditions and the following disclaimer:
405
406This software is provided by Idiomdrottning "as is" and any express or
407implied warranties, including, but not limited to, the implied
408warranties of merchantability and fitness for a particular purpose are
409disclaimed. In no event shall Idiomdrottning be liable for any direct,
410indirect, incidental, special, exemplary, or consequential damages
411(including, but not limited to, procurement of substitute goods or
412services; loss of use, data, or profits; or business interruption)
413however caused and on any theory of liability, whether in contract,
414strict liability, or tort (including negligence or otherwise) arising
415in any way out of the use of this software, even if advised of the
416possibility of such damage.
Note: See TracBrowser for help on using the repository browser.