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

Last change on this file since 40151 was 40151, checked in by Idiomdrottning, 2 months ago
File size: 10.4 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==== fn
286 (fn body ...)
287
288is shorthand for
289
290 (lambda some-basic-bindings body ...)
291
292where some-basic-bindings is one of
293
294* args
295* (x . rest)
296* (x y . rest)
297* (x y z . rest)
298
299and the fn macro automatically figures out which of those four you mean.
300
301
302==== over
303 (over body ...)
304
305is shorthand for
306
307 (cut map (lambda some-basic-bindings body ...) <>)
308
309except that the map can take any number of lists and that {{i}} is also anaphorically bound to the list index in {{body}}.
310
311Here is an example:
312
313 ((over (+ x x y i))
314  '(10 20 40) '(3 6 9))
315
316⇒ (23 47 91)
317
318
319==== as-list
320Here is a functional combinator for Scheme that lets its arguments treat their arguments as if they were lists.
321
322 ((as-list (c filter odd?)) 130752)
323
324⇒ 1375
325
326 ((as-list cdr reverse) 23311358)
327
328⇒ 5311332
329
330 ((as-list delete-duplicates) 23311358)
331
332⇒ 23158
333
334 (define (vowel? l) ((as-list (c member l)) "aeiou"))
335 ((as-list (c filter vowel?)) "magnetic mountaintop")
336
337⇒ “aeiouaio”
338
339Together with {{over}}:
340
341 ((as-list (over (l) (if (vowel? l) l (char-upcase l)))) "fleet foxes")
342
343⇒ “FLeeT FoXeS”
344
345
346==== c_r
347Sometimes you just need an arbitrarily long tree dereferencer.
348
349 (c_r cddadadaddddar)
350
351makes {{cddadadaddddar}} real. Works for any sequence of a's and d's.
352
353
354=== Making values
355
356==== with-result
357This is something that is sometimes cozy:
358
359 (with-result
360  (print 1 2 (save 3) 4 5 6)
361  (print 7 8))
362
363Prints 123456 and 78, returns 3
364
365
366==== empty?
367This is a generic predicate to see if a string is "", a list is '(), a
368number is 0 etc.
369
370
371==== {{eif}} and {{econd}}
372{{eif}} is a version of {{if}} (or, to be precise, of {{aif}} since it anaphoric) that treats empty things as falsy.
373
374 (eif "" it 'no)
375
376⇒ no
377
378{{econd}}, similarly, is an "empty is falsy" version of {{acond}}.
379
380
381==== like?
382{{like?}} is a unary curried version of {{equal?}}
383
384
385==== is?
386{{is?}} is a unary curried version of {{eq?}}
387
388
389=== Doing stuff
390
391==== for-each-line
392 (for-each-line filename body ...)
393
394{{body}} is called for its sideeffects once per line of text in
395{{filename}} with the variable {{line}} anaphorically bound to that line.
396
397For example:
398
399 (for-each-line "/tmp/foo" (print line))
400
401
402== Author
403
404[[https://idiomdrottning.org/about|Idiomdrottning]]
405
406== Source code
407
408 git clone https://idiomdrottning.org/brev-separate
409
410== License
411
412© 2021 Idiomdrottning.
413
414All rights reserved.
415
416Redistribution and use in source and binary forms, with or without
417modification, are permitted provided that the following conditions are
418met:
419
4201. Redistributions of source code must retain the above copyright
421notice, this list of conditions and the following disclaimer:
422
423This software is provided by Idiomdrottning "as is" and any express or
424implied warranties, including, but not limited to, the implied
425warranties of merchantability and fitness for a particular purpose are
426disclaimed. In no event shall Idiomdrottning be liable for any direct,
427indirect, incidental, special, exemplary, or consequential damages
428(including, but not limited to, procurement of substitute goods or
429services; loss of use, data, or profits; or business interruption)
430however caused and on any theory of liability, whether in contract,
431strict liability, or tort (including negligence or otherwise) arising
432in any way out of the use of this software, even if advised of the
433possibility of such damage.
Note: See TracBrowser for help on using the repository browser.