Changeset 40170 in project


Ignore:
Timestamp:
06/02/21 16:11:27 (12 days ago)
Author:
Idiomdrottning
Message:

Wrong url, wrong namespace etc. Was sloppy when making my auto-documentation-making–thing. Which now works, but didn't when this page was created.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wiki/eggref/5/home/sandra/skami/scm/brev-separate

    r40168 r40170  
    1 
    2 == brev-separate
    3 [[toc:]]
    4 This is {{brev-separate}}, a miscellaneous hodge-podge of macros and
    5 procedures that all have the shared aim of brevity. Sort of my take on
    6 the {{clojurian}} and {{miscmacros}} and {{(chicken base)}} genre.
    7 
    8 It's called {{brev-separate}} since the full {{brev}} egg also imports and
    9 reexports a bunch of other eggs, including the aforementioned
    10 {{clojurian}} and {{miscmacros}}.
    11 
    12 
    13 === Making macros
    14 Chicken has syntax-rules macros and ir-macros.
    15 Let's shave off some of the boiler plate so that it's easier to make macros.
    16 
    17 
    18 ==== define-syntax-rules
    19 There is already the wonderful {{define-syntax-rule}} in {{miscmacros}}
    20 but here is also {{define-syntax-rules}}:
    21 
    22  (define-syntax-rules foo ()
    23    ((foo bar) (+ bar 3))
    24    ((foo bar baz) (* bar baz)))
    25 
    26 It's just the ultra lazy person's shorthand for:
    27 
    28  (define-syntax foo
    29    (syntax-rules ()
    30      ((foo bar) (+ bar 3))
    31      ((foo bar baz) (* bar baz))))
    32 
    33 
    34 ==== define-ir-syntax*
    35 {{define-ir-syntax*}} is more interesting.
    36 
    37  (define-ir-syntax* name
    38    (pattern body) ...)
    39 
    40 It uses {{matchable}} to dispatch between different call signatures
    41 (kinda similar to how {{syntax-rules}} work) while also allowing you to
    42 {{inject}}, {{compare}}, {{strip-syntax}} and {{syntax}} inside, as per usual
    43 with ir-macros.
    44 
    45 Here's an example:
    46 
    47  (define-ir-syntax*
    48    (aif test yes no)
    49    `(let ((,(inject 'it) ,test))
    50       (if ,(inject 'it) ,yes ,no)))
    51  
    52  (aif (... expensive test ...)
    53       (car it)
    54       (print "oh! no!"))
    55 
    56 When you have multiple call-signatures, wrap pattern / body set with parens.
    57 
    58  (define-ir-syntax*
    59    ((aif #f yes no) no)
    60    ((aif test yes no)
    61     `(let ((,(inject 'it) ,test))
    62        (if ,(inject 'it) ,yes ,no))))
    63 
    64 
    65 ==== define-ir-syntax
    66 Sometimes pattern matching is overkill or you have something else in mind.
    67 
    68 {{define-ir-syntax}} macros are just
    69 
    70  (define-ir-syntax name body)
    71 
    72 where the body has access to {{body}}, {{inject}}, {{compare}},
    73 {{strip-syntax}} and {{syntax}}, as in the following example:
    74 
    75  (define-ir-syntax comp-prod
    76    (apply * body))
    77  
    78  (comp-prod 2 3 4)
    79 
    80 â‡’ 24
    81 
    82 As a rule of thumb, if you are deliberately injecting new names into
    83 the namespace that's when you are using ir-macros, and when you want
    84 to ''avoid'' doing that, use syntax-rules.
    85 
    86 
    87 === Making procedures
    88 
    89 ==== define-closure
    90  (define-closure bindings head body ...)
    91 
    92 This works like your normal
    93 
    94  (define head body ...)
    95 
    96 except that {{bindings}} are lexically closed over body.
    97 
    98  (define-closure (x 0) (counter) (inc! x))
    99  
    100  (counter) (counter) (counter)
    101 
    102 â‡’ 1 2 3
    103 
    104 The pairs of bindings aren't individual paren-wrapped, just
    105 alternating between name and expression. The set of bindings as a
    106 whole has parens.
    107 
    108  (define-closure (x 0 y 10) (jolly) (list (inc! x) (dec! y)))
    109  
    110  (jolly) (jolly) (jolly)
    111 
    112 â‡’ (1 9) (2 8) (3 7)
    113 
    114 
    115 ==== match-define
    116 {{matchable}} has {{match-lambda}} and {{match-lambda*}} as shortcuts, and
    117 they’re fantastic, but let’s also add a {{match-define}} as a shortcut
    118 on top of them.
    119 
    120 How about these sorta Haskell-style semantics?
    121 
    122  (match-define
    123   (pat body ...)
    124   ...)
    125 
    126 Here's an example:
    127 
    128  (match-define
    129     ((my-map proc ()) '())
    130     ((my-map proc (x . xs))
    131      (cons (proc x) (my-map proc xs))))
    132  
    133  (my-map - (iota 4))
    134 
    135 â‡’ (0 -1 -2 -3)
    136 
    137 Works with curried define too, of course:
    138 
    139  (match-define
    140     (((my-map proc) ()) '())
    141     (((my-map proc) (x . xs))
    142      (cons (proc x) ((my-map proc) xs))))
    143  
    144  ((my-map -) (iota 4))
    145 
    146 â‡’ (0 -1 -2 -3)
    147 
    148  (map (my-map -) '((1 2 3) (10 20 30) (100 200 300)))
    149 
    150 â‡’ ((-1 -2 -3) (-10 -20 -30) (-100 -200 -300))
    151 
    152 (Limitation: This implementation only destructures the outer argument
    153 list. For future versions it would be awesome to be able to pattern
    154 match on every level.)
    155 
    156 
    157 ==== match-define-closure
    158 Combining {{match-define}} and {{define-closure}} into one glorious
    159 Voltron!
    160 
    161  (match-define-closure bindings (pat body) ...)
    162 
    163 Here's an example:
    164 
    165  (match-define-closure
    166   (x 0)
    167   ((counter) (inc! x))
    168   ((counter 'reset) (set! x 0)))
    169  
    170  (counter) (counter) (counter 'reset) (counter) (counter)
    171 
    172 â‡’ 1 2 1 2
    173 
    174 Here is another example, the arity-table from citeSoftware Design for Flexibility.
    175 
    176  (match-define-closure
    177   (ht (make-hash-table))
    178   ((arity proc) (hash-table-ref ht proc))
    179   ((arity proc a) (hash-table-set! ht proc a)))
    180  
    181  (arity cons 2)
    182  (arity cons)
    183 
    184 â‡’ 2
    185 
    186 
    187 ==== {{call-table}}, {{call-table*}}, {{call-vector}}, {{call-string}} and {{call-list}}
    188 The previous construct is generally useful so let's just provide it as
    189 {{call-table}}.
    190 
    191  (define arity (call-table))
    192  (define color (call-table))
    193  
    194  (arity cons 2)
    195  (color cons 'blue)
    196  
    197  (map (cut <> cons) (list arity color))
    198 
    199 â‡’ (2 blue)
    200 
    201 {{call-table}} takes two optional keyword argument, {{default:}}, to set
    202 the default response for unknown keys, and {{seed:}} which can be a
    203 hash-table or an alist, and defaults to empty.
    204 
    205 There is also {{call-table*}} which by default cons its values to a list
    206 instead of replacing them. It takes four keyword arguments. {{proc:}}
    207 which defaults to {{cons}}, initial which defaults to {{'()}}, and unary
    208 which defaults to {{#f}}, and {{seed:}} as above.
    209 
    210 Both versions of call-table lets you access the underlying hash-table
    211 by calling them with no arguments, and to set them by calling them
    212 with the keyword argument {{update:}}.
    213 
    214  (color update: my-other-hash-table)
    215 
    216 [[https://idiomdrottning.org/call-table|Full documentation for call-tables]]
    217 [[https://idiomdrottning.org/call-vector|Full documentation for callable arrays]]
    218 
    219 
    220 ==== call-key*
    221 Sometimes you think {{call-table}} is convenient but you only need one key.
    222 
    223 For {{call-key}}, just use {{make-parameter}}.
    224 
    225 But {{call-key*}} is awesome since it accumulates its values.
    226 
    227 It has the same {{proc}}, {{unary}}, and {{initial}} keyword arguments as
    228 {{call-table*}}. It doesn't have {{seed}} because the idea is that you
    229 just use {{inititial}}. The generated procedure has {{update}} (which
    230 takes a new list as argument) and {{get}} (which you only need for
    231 {{unary}} call-keys).
    232 
    233  (define horses (call-key*))
    234  
    235  (horses 'ruby)
    236  (horses 'kind-girl)
    237  (horses 'tornado)
    238  
    239  (horses)
    240 
    241 â‡’ (tornado kind-girl ruby)
    242 
    243 
    244 ==== define-some
    245 This is for making functions that implicitly returns '() on an
    246 {{empty?}} first argument. In other words, it define a body for patterns
    247 with '''some''' non-empty value as first argument, hence the name
    248 {{define-some}}.
    249 
    250 For example,
    251 
    252  (define-some (descseq num)
    253     (cons num (descseq (sub1 num))))
    254 
    255 is shorthand for
    256 
    257  (define (descseq num)
    258    (if (empty? num)
    259        '()
    260        (cons num (descseq (sub1 num)))))
    261 
    262 so
    263 
    264  (descseq 5)
    265 
    266 â‡’ (5 4 3 2 1)
    267 
    268 
    269 ==== define-curry
    270 It's nice that you can make specific curries with the SRFI-219 style
    271 define heads (which is implemented per default in Chicken).
    272 
    273 That's nice if you know exactly how many stragglers and how many
    274 immediate args you have, but sometimes you need the currying itself to
    275 be arbitrary arity.
    276 
    277 Let's say you already have something like:
    278 
    279  (define (foo bar baz bax)
    280    (print baz)
    281    (+ bar baz bax))
    282 
    283 but you realize you need arbitrary-arity currying.
    284 
    285 Just change it to use {{define-curry}} instead of {{define}}:
    286 
    287  (define-curry (foo bar baz bax)
    288    (print baz)
    289    (+ bar baz bax))
    290  
    291  (=
    292   (foo 100 20 3)
    293   ((foo 100) 20 3)
    294   ((foo 100 20) 3)
    295   ((foo) 100 20 3)
    296   (((foo) 100) 20 3)
    297   (((foo 100) 20) 3)
    298   ((((foo) 100) 20) 3))
    299 
    300 Prints seven 20 and returns {{#t}}.
    301 
    302 It only works when {{foo}} otherwise would have fixed arity.
    303 
    304 
    305 ==== {{c}} a.k.a. {{🍛}} a.k.a. {{@>}}
    306 This isn't the traditional c-combinator from mockingbirds and such.
    307 It's just a one-letter spelling of "curry". It's a function
    308 combinator.
    309 
    310  ((c + 1 20 300) 4000 50000)
    311 
    312 â‡’ 54321
    313 
    314 I also exported it using the name 🍛 for those who find emoji names
    315 more comfortable to use due to namespace issues.
    316 
    317 I later found out that {{@>}} from the {{holes}} egg is same the
    318 combinator as this.
    319 
    320 It has arbitrary arity and can work on arbitrary arity functions, but
    321 isn't recursive to multiple levels.
    322 
    323 
    324 ==== fn
    325  (fn body ...)
    326 
    327 is shorthand for
    328 
    329  (lambda some-basic-bindings body ...)
    330 
    331 where some-basic-bindings is one of
    332 
    333 * args
    334 * (x . rest)
    335 * (x y . rest)
    336 * (x y z . rest)
    337 
    338 and the fn macro automatically figures out which of those four you
    339 mean.
    340 
    341 
    342 ==== over
    343  (over body ...)
    344 
    345 is shorthand for
    346 
    347  (cut map (lambda some-basic-bindings body ...) <>)
    348 
    349 except that the map can take any number of lists and that {{i}} is also
    350 anaphorically bound to the list index in {{body}}.
    351 
    352 Here is an example:
    353 
    354  ((over (+ x x y i))
    355   '(10 20 40) '(3 6 9))
    356 
    357 â‡’ (23 47 91)
    358 
    359 
    360 ==== as-list
    361 Here is a functional combinator for Scheme that lets its arguments
    362 treat their arguments as if they were lists.
    363 
    364  ((as-list (c filter odd?)) 130752)
    365 
    366 â‡’ 1375
    367 
    368  ((as-list cdr reverse) 23311358)
    369 
    370 â‡’ 5311332
    371 
    372  ((as-list delete-duplicates) 23311358)
    373 
    374 â‡’ 23158
    375 
    376  (define (vowel? l) ((as-list (c member l)) "aeiou"))
    377  ((as-list (c filter vowel?)) "magnetic mountaintop")
    378 
    379 â‡’ “aeiouaio”
    380 
    381 Together with {{over}}:
    382 
    383  ((as-list (over (if (vowel? x) x (char-upcase x)))) "fleet foxes")
    384 
    385 â‡’ “FLeeT FoXeS”
    386 
    387 
    388 ==== make-tree-accessors
    389 Sometimes you just need an arbitrarily long tree dereferencer.
    390 
    391  (c_r cddadadaddddar)
    392 
    393 makes {{cddadadaddddar}} real. Works for any sequence of a's and d's.
    394 
    395 
    396 ==== make-sloppy-tree-accessors
    397 As above, but uses scar and scdr instead of car and cdr.
    398 
    399 
    400 === Making values
    401 
    402 ==== with-result
    403 This is something that is sometimes cozy:
    404 
    405  (with-result
    406   (print 1 2 (save 3) 4 5 6)
    407   (print 7 8))
    408 
    409 Prints 123456 and 78, returns 3
    410 
    411 
    412 ==== aif-with-result
    413  (aif-with-result (odd? (save 3)) (+ it 4) (+ it 1000))
    414 
    415 â‡’ 7
    416 
    417 That tests the entire expression but only stores the saved part into {{it}}.
    418 
    419 Combining {{aif}} and {{with-result}} would do the opposite:
    420 
    421  (aif
    422   (with-result (pred? (save 27)))
    423   it #f)
    424 
    425 That tests 27 and stores 27 into {{it}}, while the pred? call is thrown away.
    426 
    427 
    428 ==== empty?
    429 This is a generic predicate to see if a string is "", a list is '(), a
    430 number is 0 etc.
    431 
    432 
    433 ==== {{eif}} and {{econd}}
    434 {{eif}} is a version of {{if}} (or, to be precise, of {{aif}} since it
    435 anaphoric) that treats empty things as falsy.
    436 
    437  (eif "" it 'no)
    438 
    439 â‡’ no
    440 
    441 {{econd}}, similarly, is an "empty is falsy" version of {{acond}}.
    442 
    443 
    444 ==== like?
    445 {{like?}} is a unary curried version of {{equal?}}
    446 
    447 
    448 ==== is?
    449 {{is?}} is a unary curried version of {{eq?}}
    450 
    451 
    452 ==== {{scdr}}, and {{scar}}
    453 A scar is like a car but returns '() if the pair has no car. A scdr is
    454 like a cdr but returns '() if the pair has no cdr.
    455 
    456 
    457 ==== realpath
    458  (realpath filename)
    459 
    460 Uses current-directory to try to figure out an absolute path for
    461 filename.
    462 
    463 
    464 === Doing stuff
    465 
    466 ==== for-each-line
    467  (for-each-line filename body ...)
    468 
    469 {{body}} is called for its sideeffects once per line of text in
    470 {{filename}} with the variable {{line}} anaphorically bound to that line.
    471 
    472 For example:
    473 
    474  (for-each-line "/tmp/foo" (print line))
    475 
    476 
    477 === Source code
    478  git clone https://idiomdrottning.org/brev-separate
    479 
Note: See TracChangeset for help on using the changeset viewer.