Changeset 40390 in project


Ignore:
Timestamp:
08/25/21 06:15:37 (4 weeks ago)
Author:
Idiomdrottning
Message:

Swap out documentation completely

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wiki/eggref/5/match-generics

    r40134 r40390  
    1 == match-generics
     1
     2== The fanciest define of all time
    23[[toc:]]
     4A define that can destructure, pattern match, be nested, and create generics!
    35
    4 Here are some flexible and fast generics.
     6 (define (foop so much #!key (insight 2) (joy 3)) (list so much insight joy))
     7 (define (foop (? string? apples) (? string? horses)) (string-append horses apples))
     8 (list (foop "way" "no ") (foop 3 2 joy: 1))
    59
    6 They don't need to have the same number of arguments or use the same
    7 names:
     10⇒ ("no way" (3 2 2 1))
    811
    9  (define-generic (frotz a b c) (list b a c))
    10  (define-generic (frotz x y) (+ x y))
    11  (list (frotz 1 2 3) (frotz 4 5))
     12It works.♥
    1213
    13 â‡’ ((2 1 3) 9)
     14When there's just a normal define (with or without DSSSL stuff), it
     15expands to a normal define. Using that variable or calling that function doesn't have any overhead, it's just bound normally.
    1416
    15 They can even destructure their arguments:
     17Then when you define more stuff, it changes to become a dispatching generic multimethod.
    1618
    17  (define-generic (my-map proc (x . xs))
    18    (cons (proc x) (my-map proc xs)))
     19One normal define becomes a normal define. It can have DSSSL stuff such as keyword arguments, that's fine.
     20
     21Two defines (normal or not), or one un-normal define (using destructuring for example), and it switches to matching.
     22
     23 (define (just-a-single (oh my what)) (list my what oh))
    1924 
    20  (define-generic (my-map proc ()) '())
    21  
     25 (just-a-single (string->list "hey"))
     26
     27⇒ (#\e #\y #\h)
     28
     29Here is how the {{my-map}} example I've been using looks like in this system:
     30
     31 (define (my-map proc (x . xs)) (cons (proc x) (my-map proc xs)))
     32 (define (my-map proc ()) '())
    2233 (my-map add1 '(1 2 3))
    2334
    2435⇒ (2 3 4)
    2536
    26 To use predicates, use the form {{(? pred var)}}, like this:
    2737
    28  (define-generic (plus (? list? a) (? list? b)) (append a b))
    29  
    30  (define-generic (plus (? string? a) (? string? b)) (string-append a b))
    31  
    32  (define-generic (plus (? number? a) (? number? b)) (+ a b))
    33  
    34  (list
    35   (plus "13" "14")
    36   (plus 7 22)
    37   (plus 13 19)
    38   (plus '(a b c) '(1 2 3)))
     38=== Lovable quirks
    3939
    40 â‡’ ("1314" 29 32 (a b c 1 2 3))
    41 
    42 You need to define them in order from least specific to most specific,
    43 i.e. define the most specifics last and the fallbacks first.
    44 
    45 You can even use nested defines! But if you do, each need to use the
    46 same car positions. Only the cdr position (a.k.a the arguments) is
    47 generic.
    48 
    49  (define-generic ((frobnicate a) b z) (list a b z))
    50  (define-generic ((frobnicate a) b) (string-append a b))
    51  
    52  (let ((hoho (frobnicate "hoho")))
    53    (list (hoho 1 2)
    54          (hoho " and such")))
    55 
    56 â‡’ (("hoho" 1 2) "hoho and such")
     40==== Precedence order
     41LIFO baby! Most recently defined is checked first.
    5742
    5843
    59 === Implementation
    60 By now, maybe some of the long-time readers in the audience has
    61 guessed what is going on.
     44==== Nesting in my car
     45Nesting in the car position (for currying purps) works fine, but they
     46must all look the same. You can't destructure in there, only in the
     47outermost position a.k.a. the cdr position a.k.a. the args. The car
     48positions must be exactly the same (their code/​symbolic/​read
     49representation must be {{equal?}}) including variable names.
    6250
    63 Each generic is defined using a clause from [[http://wiki.call-cc.org/eggref/5/matchable|matchable]]!
     51The older ones aren't thrown away, exactly. The most recent one and
     52everything older that is equal? are what counts. (This isn't slow, it
     53uses a hash-table to sort that out.)
    6454
    65 The implementation using some miscellaneous [[https://idiomdrottning.org/brev-separate|brev-separate]] stuff is just
 '''five lines!'''
     55 (define ((foo a) b c d) (* a b c d))
     56 (define ((foo a b) c) (list a b c)); ← This one won't count
     57 (define ((foo a) b c) (+ a b c)); ← because this one came after
     58 
     59 (list ((foo 3) 2 3) ((foo 3) 2 3 8))
    6660
    67  (define-for-syntax gentable (call-table*))
    68  (define-ir-syntax*
    69    (define-generic (name . pattern) . body)
    70    (cons 'match-define
    71          (gentable (strip-syntax name) (cons (cons name pattern) body)))))
     61⇒ (8 144)
    7262
    73 For source code,
    7463
    75  git clone https://idiomdrottning.org/match-generics
     64==== Defining variables
     65If you just define a plain vanilla variable, then there's not gonna be
     66any dispatching there:
    7667
     68 (define horses 23)
     69
     70Any older stuff you had stored in {{horses}} isn't lost, it'll be ready
     71for when you define a new generic using their same car signature.
     72
     73The same goes for
     74
     75 (define horses (lambda () 23))
     76
     77That's not gonna be part of a generic.
     78
     79"Single variables" like this aren't saved, and can be overwritten on
     80next call to {{define}}.
     81
     82
     83==== Resetting & hacking
     84"OMG, I sent a mistyped definition to the REPL and now I can't define
     85over it since this new define just adds stuff!"
     86
     87This doesn't work:
     88
     89 (define (wrong tyop) typo)
     90 (define wrong #f)
     91 (define (wrong typo) typo)
     92
     93It'll have both the {{tyop}} and the {{typo}} version in there.
     94
     95I mean, that'd kind of be fine in this particular case since the
     96dispatcher will see the latest version first and that happens to
     97shadow the typos, but, if you put typos in predicates or whatever you
     98might be in for a rough time.
     99
     100However, to reset when you mistype something and wanna re-define at
     101the REPL, do {{(define reset: <the-name>)}}, so in this example
     102
     103 (define reset: wrong)
     104
     105And to get access to the entire underlying call-table* for your own
     106metamagic and hackery purps just use {{(define)}}.
     107
     108We wanna make easy things easy but any thing possible.♥
     109
     110Emacs nerds, here is an elisp function that resets a define you're in:
     111
     112 (defun mdg-reset-define ()
     113   (interactive)
     114   (save-excursion
     115     (re-search-backward "^(define ")
     116    (forward-char 1)
     117    (re-search-forward "(")
     118    (let* ((end (1- (re-search-forward "[ )]")))
     119       (beg (1+ (re-search-backward "(")))
     120       (str (buffer-substring beg end)))
     121      (comint-send-string
     122       (scheme-proc)
     123       (format "(define reset: %s)\n" str))
     124      (message "Resetting %s" str))))
     125
     126Bind it to whatever. You then need to re-send the sexps you do want.
     127
     128
     129==== The vagaries of DSSSL
     130You can't combine DSSSL (i.e. {{#!key}}, {{#!optional}} and {{#!rest}}) and
     131matchable in the same line, but, as you saw in the {{foop}} example
     132above, the same generic can handle both DSSSL stuff and matchable just
     133fine, in separate clauses.
     134
     135The dispatcher sees all of that stuff as just one big {{. rest}} tail so
     136it catches and collides with a lot of patterns. So be careful if you
     137wanna combine DSSSL and generics.
     138
     139
     140=== Source code
     141This define is available under the name {{define-dx}} in the
     142[[/match-generics|match-generics]] egg.
     143
     144 git source https://idiomdrottning.org/match-generics
     145
     146Inside of the [[/brev|brev]] repo, and provided with the big {{brev}} egg, there
     147is an extension named {{mdg}} that imports it under the name {{define}}
     148and the OG define as {{define-og}}. So just {{(import brev mdg)}}, which is
     149now part of the little {{brev}} compiler shell wrapper and is prompted
     150for by the {{brev2scm}} conversion process. Or if you want just the define
     151and not the rest of the brev batteries,
     152
     153 (import (rename match-generics (define-dx define)))
     154
Note: See TracChangeset for help on using the changeset viewer.