* [PATCH v3] docs/match: pattern matcher example makeover
@ 2023-02-01 13:09 Blake Shaw
2023-02-01 16:40 ` Maxime Devos
0 siblings, 1 reply; 8+ messages in thread
From: Blake Shaw @ 2023-02-01 13:09 UTC (permalink / raw)
To: guile-devel; +Cc: Blake Shaw
[V3 update]
@xref{sxml-match} had been commented out because it was interfering
with emacs makeinfo. This ammends that change.
Also adding `--` to breakup changelog into a more digestible format.
Reviewing everything all else, everything appears fine.
[V2 update]
All changes have been squashed into a single commit
Before I was able to render only the pdf, but it was unclear
how to render an individual texinfo page. Since then I discovered
[makeinfo] in emacs which solves this problem.
There is some conflict between PDF and Texinfo, and Guile currently
seems to just choose texinfo. This update to the past patch series
tailors the markup to best suit Texinfo, where previously PDF was
was the rendered reference to compare against.
-
docs/match: add pattern matching examples
--
This commit introduces a set of examples to the documentation on
pattern matching discussed in the Guix Days presentation which can
be found here:
https://xana.lepiller.eu/guix-days-2022/guix-days-2022-documentation.mp4
As discussed in the Guix Days presentation, and agreed upon during the
corresponding brainstorming session, I'm introducing a set of examples
that intend to demonstrate the basics of the Shinn/Wright pattern
matcher, as well as touch on some of its subtleties.
Most paragraphs will be broken up into smaller peices with examples
interspersed throughout, according to the refactoring strategy of
"graduated expression" presented in my talk. The goal is to express the
pedagogical aim of examples naturally through graduality, reducing
"wordiness" primarily by with examples rather than reducing what has
already been written.
My apologies for taking so long to get to work on this! This summer
I was confronted with a unanticipated event that subsequently turned
my life upside down, causing my contributions to be low. I hope to be
increasing my contributions from here on, and fulfilling my obligations
concerning the documentation.
-
docs/match: rm unquote-splicing as it interferes with textinfo
--
-
docs/match: add reverse nested list example
--
-
docs/match: match-let* unwrap example
--
pdf now builds, but there are some spacing issues I want to correct
in the PDF, that will be introduced next.
-
docs/fixup: @cindex was in the wrong place
--
-
style: clean-up newlines
--
It appears that while the PDF needs additional newlines
to be presentable, these appear to have a negative effect
on the presentation of the texinfo doc.
I don't know how to fix this, but from looking at the PDF,
it appears that the strategy until now has been to privilege
texinfo at the expense of PDF readability (the PDF is more
or less "squished together")
So in that regard, these edits make my past edits more in sync
with past Guile docs.
-
examples: replace with didactic ex. that can copied & pasted
--
The existing example can't be copied and pasted.
This example both fixes the past one and improves on its relation
to the text.
-
style: switch to "Indiana style", bracketing lets and clauses
--
After spending much time looking at the examples in black & white
to edit the texinfo document, it occurred to me just how much the
brackets improve legibility. Therefore, I have decided to adopt
the "Indiana" style of using brackets, used by Kent Dybvig, Dan
Friedman and Will Byrd at Indiana University.
Currently the docs use this style in some places but not in others.
Considering some are color blind, and that few will have rigged
their texinfo configuration to use rainbow-delimiters in the while
reading documentation, I think this should be considered a general
accessibility improvement.
indentation: make consistent according to rule defined below
If a new paragraph opens onto a new topic, it should naturally
indent (i.e, no indentation markup is required)
If a new paragraph is a continuation of the current subject,
the markup @noident should be applied
markup: replace @var with @code unless @var is a @defn argument
The way that it renders in texinfo means that it renders @vars
in uppercase, the way that is conventionally done for definition
arguments.
Therefore I've changed all @vars to @code unless @var is a @defn
argument
-
remove: paragraph that referred to a since removed example
--
fix: uncomment @xref{sxml-match}
---
doc/ref/match.texi | 252 ++++++++++++++++++++++++++++++---------------
1 file changed, 167 insertions(+), 85 deletions(-)
diff --git a/doc/ref/match.texi b/doc/ref/match.texi
index f5ea43118..4e657b976 100644
--- a/doc/ref/match.texi
+++ b/doc/ref/match.texi
@@ -23,71 +23,142 @@ The @code{(ice-9 match)} module provides a @dfn{pattern matcher},
written by Alex Shinn, and compatible with Andrew K. Wright's pattern
matcher found in many Scheme implementations.
-@cindex pattern variable
-A pattern matcher can match an object against several patterns and
-extract the elements that make it up. Patterns can represent any Scheme
-object: lists, strings, symbols, records, etc. They can optionally contain
-@dfn{pattern variables}. When a matching pattern is found, an
-expression associated with the pattern is evaluated, optionally with all
-pattern variables bound to the corresponding elements of the object:
+@noindent A pattern matcher does precisely what the name implies: it
+matches some arbitrary pattern, and returns some result accordingly.
@example
-(let ((l '(hello (world))))
- (match l ;; <- the input object
- (('hello (who)) ;; <- the pattern
- who))) ;; <- the expression evaluated upon matching
-@result{} world
+(define (english-base-ten->number name)
+ (match name
+ ('zero 0)
+ ('one 1)
+ ('two 2)
+ ('three 3)
+ ('four 4)
+ ('five 5)
+ ('six 6)
+ ('seven 7)
+ ('eight 8)
+ ('nine 9)))
+
+(english-base-ten->number 'six)
+@result{} 6
+
+(apply + (map english-base-ten->number '(one two three four)))
+@result{} 10
@end example
-In this example, list @var{l} matches the pattern @code{('hello (who))},
-because it is a two-element list whose first element is the symbol
-@code{hello} and whose second element is a one-element list. Here
-@var{who} is a pattern variable. @code{match}, the pattern matcher,
-locally binds @var{who} to the value contained in this one-element
-list---i.e., the symbol @code{world}. An error would be raised if
-@var{l} did not match the pattern.
+@page
+@cindex pattern variable
+@noindent Pattern matchers may contain @dfn{pattern variables},
+local bindings to all elements that match a pattern.
-The same object can be matched against a simpler pattern:
+@example
+(let re ([ns '(one two three four 9)] [total 0])
+ (match ns
+ [(e) (+ total (english-base-ten->number e))]
+ [(e . es)
+ (re es (+ total (english-base-ten->number e)))]))
-@example
-(let ((l '(hello (world))))
- (match l
- ((x y)
- (values x y))))
-@result{} hello
-@result{} (world)
+@result{} 19
@end example
-Here pattern @code{(x y)} matches any two-element list, regardless of
-the types of these elements. Pattern variables @var{x} and @var{y} are
-bound to, respectively, the first and second element of @var{l}.
-
-Patterns can be composed, and nested. For instance, @code{...}
+@noindent In this example, the list @code{ns} matches the pattern
+@code{(e . es)}, where the pattern variable @code{e} corresponds
+to the metaphoical "car" of @code{ns} and the pattern variable @code{es}
+corresponds to the "cdr" of @code{ns}.
+
+@noindent A tail call @code{re} is then initiated and we "cdr" down the
+list by recurring on the tail @code{es}, applying our matcher
+@code{english-base-ten->number} to each element of @code{ns} until
+only a single element @code{(e)} remains, causing the @code{total}
+to be computed. In modern Scheme programming it is common to use
+@code{match} in place of the more verbose but familiar combination
+of @code{cond}, @code{car} and @code{cdr}, so it's important to
+understand how these idioms translate.
+
+Patterns can be composed and nested. For instance, @code{...}
(ellipsis) means that the previous pattern may be matched zero or more
times in a list:
@example
-(match lst
- (((heads tails ...) ...)
- heads))
+(match '((a.0 b.0 c.0 ((1.0 2.0 3.0) x.0 y.0 z.0))
+ (a.1 b.1 c.1 ((1.1 2.1 3.1) x.1 y.1 z.1)))
+ [((heads ... ((tails ...) . rest)) ...)
+ (begin
+ (format #t "heads: ~a ~%" heads)
+ (format #t "tails: ~a ~%" tails)
+ (format #t "rest: ~a ~%" rest))])
+@result{}
+heads: ((a.0 b.0 c.0) (a.1 b.1 c.1))
+tails: ((1.0 2.0 3.0) (1.1 2.1 3.1))
+rest: ((x.0 y.0 z.0) (x.1 y.1 z.1))
@end example
-@noindent
-This expression returns the first element of each list within @var{lst}.
-For proper lists of proper lists, it is equivalent to @code{(map car
-lst)}. However, it performs additional checks to make sure that
-@var{lst} and the lists therein are proper lists, as prescribed by the
-pattern, raising an error if they are not.
-
-Compared to hand-written code, pattern matching noticeably improves
-clarity and conciseness---no need to resort to series of @code{car} and
-@code{cdr} calls when matching lists, for instance. It also improves
-robustness, by making sure the input @emph{completely} matches the
-pattern---conversely, hand-written code often trades robustness for
-conciseness. And of course, @code{match} is a macro, and the code it
-expands to is just as efficient as equivalent hand-written code.
-
-The pattern matcher is defined as follows:
+@noindent A pattern matcher can match an object against several
+patterns and extract the elements that make it up.
+
+@example
+(match '((l1 . r1) (l2 . r2) (l3 . r3))
+ [((left . right) ...)
+ (list left right)])
+
+@result{} ((l1 l2 l3) (r1 r2 r3))
+@end example
+
+@example
+(match '((1 . (a . b)) (2 . (c . d)) (3 . (e . f)))
+ [((key . (left . right)) ...)
+ (fold-right acons '() key right )])
+
+@result{} ((1 . b) (2 . d) (3 . f))
+@end example
+
+@example
+(match '(((a b c) e f g) 1 2 3)
+ [(((head ...) . rest) tails ...)
+ (acons tails head rest )])
+
+@result {} (((1 2 3) a b c) e f g)
+@end example
+
+Patterns can represent any Scheme object: lists, strings, symbols,
+records, etc.
+
+@noindent When a matching pattern is found, an expression is evaluated
+with pattern variables bound to the corresponding elements of the object.
+
+@example
+(let re ([m #(a "b" c "d" e "f" g)])
+ (match m
+ [(or (e) #(e)) e]
+ [(or #(e1 e2 es ...)
+ (e1 e2 es ...))
+ (cons (cons e1 e2)
+ (re es))]))
+
+@result{} ((a . "b") (c . "d") (e . "f") . g)
+@end example
+
+@example
+(let re ([m '(a b c d e f g h i)])
+ (match m
+ [(e) e]
+ [(e1 e2 es ...)
+ (acons e1 e2 (re es))]))
+
+@result{} ((a . b) (c . d) (e . f) (g . h) . i)
+@end example
+
+@noindent Compared to hand-written code, pattern matching noticeably
+improves clarity and conciseness---no need to resort to series of
+@code{car} and @code{cdr} calls when matching lists, for instance.
+It also improves robustness, by making sure the input @emph{completely}
+matches the pattern---conversely, hand-written code often trades
+robustness for conciseness. And of course, @code{match} is a macro,
+and the code it expands to is just as efficient as equivalent
+hand-written code.
+
+@noindent We define @code{match} as follows: @*
@deffn {Scheme Syntax} match exp clause1 clause2 @dots{}
Match object @var{exp} against the patterns in @var{clause1}
@@ -96,9 +167,9 @@ value produced by the first matching clause. If no clause matches,
throw an exception with key @code{match-error}.
Each clause has the form @code{(pattern body1 body2 @dots{})}. Each
-@var{pattern} must follow the syntax described below. Each body is an
+@code{pattern} must follow the syntax described below. Each body is an
arbitrary Scheme expression, possibly referring to pattern variables of
-@var{pattern}.
+@code{pattern}.
@end deffn
@c FIXME: Document other forms:
@@ -114,7 +185,7 @@ arbitrary Scheme expression, possibly referring to pattern variables of
@c
@c clause ::= (pat body) | (pat => exp)
-The syntax and interpretation of patterns is as follows:
+@noindent @* The pattern language is specified as follows: @*
@verbatim
patterns: matches:
@@ -176,12 +247,12 @@ qp ::= () the empty list
| ,@pat a pattern
@end verbatim
-The names @code{quote}, @code{quasiquote}, @code{unquote},
+@noindent The names @code{quote}, @code{quasiquote}, @code{unquote},
@code{unquote-splicing}, @code{?}, @code{_}, @code{$}, @code{and},
@code{or}, @code{not}, @code{set!}, @code{get!}, @code{...}, and
@code{___} cannot be used as pattern variables.
-Here is a more complex example:
+Here is a more complex example using records and promises:
@example
(use-modules (srfi srfi-9))
@@ -193,24 +264,24 @@ Here is a more complex example:
(name person-name)
(friends person-friends))
- (letrec ((alice (make-person "Alice" (delay (list bob))))
- (bob (make-person "Bob" (delay (list alice)))))
+ (letrec ([alice (make-person "Alice" (delay (list bob)))]
+ [bob (make-person "Bob" (delay (list alice)))])
(match alice
- (($ person name (= force (($ person "Bob"))))
- (list 'friend-of-bob name))
- (_ #f))))
+ [($ person name (= force (($ person "Bob"))))
+ (list 'friend-of-bob name)]
+ [_ #f])))
@result{} (friend-of-bob "Alice")
@end example
@noindent
Here the @code{$} pattern is used to match a SRFI-9 record of type
-@var{person} containing two or more slots. The value of the first slot
-is bound to @var{name}. The @code{=} pattern is used to apply
+@code{person} containing two or more slots. The value of the first slot
+is bound to @code{name}. The @code{=} pattern is used to apply
@code{force} on the second slot, and then checking that the result
matches the given pattern. In other words, the complete pattern matches
-any @var{person} whose second slot is a promise that evaluates to a
-one-element list containing a @var{person} whose first slot is
+any @code{person} whose second slot is a promise that evaluates to a
+one-element list containing a @code{person} whose first slot is
@code{"Bob"}.
The @code{(ice-9 match)} module also provides the following convenient
@@ -229,11 +300,11 @@ expressions.
@end deffn
@example
-((match-lambda
- (('hello (who))
- who))
- '(hello (world)))
-@result{} world
+(define flatten-singletons
+ (match-lambda [((s) ...) s]))
+
+(flatten-singletons '((x) (y) (z)))
+@result{} (x y z)
@end example
@deffn {Scheme Syntax} match-lambda* clause1 clause2 @dots{}
@@ -264,11 +335,10 @@ and can also be used for recursive functions which match on their
arguments as in @code{match-lambda*}.
@example
-(match-let (((x y) (list 1 2))
- ((a b) (list 3 4)))
- (list a b x y))
-@result{}
-(3 4 1 2)
+(match-let ([(x y ...) (list 1 2 3)]
+ [(a b ...) (list 3 4 5)])
+ (list x a y b))
+@result{} (1 3 (2 3) (4 5))
@end example
@end deffn
@@ -287,22 +357,34 @@ Similar to @code{match-let}, but analogously to @code{let*}, match and
bind the variables in sequence, with preceding match variables in scope.
@example
-(match-let* (((x y) (list 1 2))
- ((a b) (list x 4)))
- (list a b x y))
+(match-let* ([(x . y) (list 1 2 3)]
+ [(a . b) (list x 4 y)])
+ (list a b))
@equiv{}
-(match-let (((x y) (list 1 2)))
- (match-let (((a b) (list x 4)))
- (list a b x y)))
-@result{}
-(1 4 1 2)
+(match-let ([(x . y) (list 1 2)])
+ (match-let ([(a . b) (list x 4 y)])
+ (list a b)))
+@result{} (1 (4 (2 3)))
@end example
@end deffn
+@example
+(define wrap '(((((unnest arbitrary nestings))))))
+
+(let unwrap ([peel wrap])
+ (match-let* ([([core ...]) peel]
+ [(wrapper ...) core])
+ (if (> (length wrapper) 1)
+ wrapper
+ (unwrap wrapper))))
+
+@result{} (unnest arbitrary nestings)
+@end example
+
@deffn {Scheme Syntax} match-letrec ((variable expression) @dots{}) body
Similar to @code{match-let}, but analogously to @code{letrec}, match and
bind the variables with all match variables in scope.
@end deffn
-Guile also comes with a pattern matcher specifically tailored to SXML
-trees, @xref{sxml-match}.
+@noindent Guile also comes with a pattern matcher specifically
+tailored to SXML trees, @xref{sxml-match}.
--
2.39.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-01 13:09 [PATCH v3] docs/match: pattern matcher example makeover Blake Shaw
@ 2023-02-01 16:40 ` Maxime Devos
2023-02-02 17:47 ` David Pirotte
0 siblings, 1 reply; 8+ messages in thread
From: Maxime Devos @ 2023-02-01 16:40 UTC (permalink / raw)
To: Blake Shaw, guile-devel
[-- Attachment #1.1.1: Type: text/plain, Size: 22572 bytes --]
On 01-02-2023 14:09, Blake Shaw wrote:
> [...]
> -
> style: clean-up newlines
> --
> It appears that while the PDF needs additional newlines
> to be presentable, these appear to have a negative effect
> on the presentation of the texinfo doc.
>
> I don't know how to fix this, but from looking at the PDF,
> it appears that the strategy until now has been to privilege
> texinfo at the expense of PDF readability (the PDF is more
> or less "squished together")
>
> So in that regard, these edits make my past edits more in sync
> with past Guile docs.
IIRC, Texinfo has a @iftex @endif construct or such. You could use this
to define a @pdf-newline macro, to only insert newlines in the PDF (TeX
is used for the PDF).
> -
> examples: replace with didactic ex. that can copied & pasted
> --
> The existing example can't be copied and pasted.
>
> This example both fixes the past one and improves on its relation
> to the text.
>
> -
> style: switch to "Indiana style", bracketing lets and clauses
> --
> After spending much time looking at the examples in black & white
> to edit the texinfo document, it occurred to me just how much the
> brackets improve legibility. Therefore, I have decided to adopt
> the "Indiana" style of using brackets, used by Kent Dybvig, Dan
> Friedman and Will Byrd at Indiana University.
>
> Currently the docs use this style in some places but not in others.
>
> Considering some are color blind, and that few will have rigged
> their texinfo configuration to use rainbow-delimiters in the while
> reading documentation, I think this should be considered a general
> accessibility improvement.
IME, (( )) is quite readable (and I don't use rainbow delimiters).
That might largely be 'due to experience', though. While I would
expect ([ ] [ ]) to be unconventional for many Guilers, it should be
readable too though, so I suppose it could be good to just change the
convention, then.
You are currently making the manual more inconsistent by using this (for
Guile) mostly non-standard notation though; IIRC the manual mostly does
(( )) and not ([ ]). Yet, in the review of the v1, you mentioned
> No, I'm not, I'm being totally boring and normal in this regard because collectively authored documentation is something you should never adopt non-standard writing notation in the course of authoring, just to one up someone on a mailing list
>
> To be honest, it's this kind of attitude that has resulted in the current docs that so many people find utterly incomprehensible. The core point of my talk that what makes Info Guile so hard to read is the lack of stylistic consistency. Editors and editing exist for a very good reason.
, which is very much against non-standard notation and for consistency.
As such, I propose:
a) Before (or after) this patch, change everything in the manual to
"Indiana style", for consistency. If you go for 'after this
patch', I mean immediately afterwards, because Guile contributors
tend to come and go, and delaying things tends to become never
doing things.
b) or: do it in non-Indiana style (likely not the option you will
take, but it would be more stylistically consistent than the
current version of the patch ...)
c) or: don't adjust everything in the manual to Indiana style yet,
but also make it a rule that the manual (and Guile code in Guile
proper, I guess) does Indiana style, and that all current
deviations from Indiana style are old style to be updated in the
future.
If this were Guix, you could make this a rule by adding it
to the "Contributing" section. Guile does not have appear to have
such a section, but "1.8 Typograhical Conventions" might be a good
place.
Additionally, changing the parenthesis convention in Guile is not just a
change to the 'match' documentation, but the subject line only mentions
'match'. While Indiana styles seems a good thing to me at first sight
now you mention the benefits, it needs a separate e-mail thread such
that people interested in ()/[] stuff but not in 'match' stuff will have
an opportunity to respond.
> indentation: make consistent according to rule defined below
>
> If a new paragraph opens onto a new topic, it should naturally
> indent (i.e, no indentation markup is required)
>
> If a new paragraph is a continuation of the current subject,
> the markup @noident should be applied
>
> markup: replace @var with @code unless @var is a @defn argument
>
> The way that it renders in texinfo means that it renders @vars
> in uppercase, the way that is conventionally done for definition
> arguments.
I'm not too familiar with Texinfo PDF output but I'll take your word for
it. However, this is not the case at least for HTML output, as you can
see at
<https://www.gnu.org/software/texinfo/manual/texinfo/html_node/_0040var.html>,
for HTML documentation it remains lowercase.
> Therefore I've changed all @vars to @code unless @var is a @defn
> argument
I'm missing what you mean with the 'Therefore'. How does this relate to
your previous paragraph (I don't get what your point is about
'definition arguments')? Do you mean that uppercase @var bad and that
it should be lowercase instead? If so, it would be better to modify
Texinfo itself to let @var not change the case, then every manual in
Texinfo would benefit instead of only the Guile manual.
Also, you could ask the Texinfo people if there is a reason for
uppercase @var; maybe they determined that it is more readable to more
people (I'm just speculating, I don't know the reason)? -- Presumably
there's some good reason (or maybe not, I don't know, but you could ask
them first).
Otherwise, if you make this Guile-specific change, you would create
stylistical inconsistencies between projects using Texinfo. More
specifically, you are creating stylistical inconsisencies between GNU
projects.
Additionally, you are not merely removing the uppercasing thing, you are
also removing the 'slanted' thing -- the result of @var is slanted
typewriter, the result of @code is merely typewriter, which makes it
slightly harder to distinguish metavariables from other code.
You are also only making this stylistical change in the documentation of
'match'; the remainder of the manual still has the old @var. If you
change tings, it would be better to change things for the whole manual.
I think you can do this by redefining the @var macro to whatever you
want in the prelude (at least that can be done in TeX).
> -
> remove: paragraph that referred to a since removed example
> --
>
> fix: uncomment @xref{sxml-match}
> ---
> doc/ref/match.texi | 252 ++++++++++++++++++++++++++++++---------------
> 1 file changed, 167 insertions(+), 85 deletions(-)
>
> diff --git a/doc/ref/match.texi b/doc/ref/match.texi
> index f5ea43118..4e657b976 100644
> --- a/doc/ref/match.texi
> +++ b/doc/ref/match.texi
> @@ -23,71 +23,142 @@ The @code{(ice-9 match)} module provides a @dfn{pattern matcher},
> written by Alex Shinn, and compatible with Andrew K. Wright's pattern
> matcher found in many Scheme implementations.
>
> -@cindex pattern variable
> -A pattern matcher can match an object against several patterns and
> -extract the elements that make it up. Patterns can represent any Scheme
> -object: lists, strings, symbols, records, etc. They can optionally contain
> -@dfn{pattern variables}. When a matching pattern is found, an
> -expression associated with the pattern is evaluated, optionally with all
> -pattern variables bound to the corresponding elements of the object:
> +@noindent A pattern matcher does precisely what the name implies: it
> +matches some arbitrary pattern, and returns some result accordingly.
Again, as I mentioned previously, in the general case it matches
arbitrary patterns (plural) and returns results (plural) -- the 'match'
construct is not as limited as you are implying it to be here.
>
> @example
> -(let ((l '(hello (world))))
> - (match l ;; <- the input object
> - (('hello (who)) ;; <- the pattern
> - who))) ;; <- the expression evaluated upon matching
> -@result{} world
> +(define (english-base-ten->number name)
> + (match name
> + ('zero 0)
> + ('one 1)
> + ('two 2)
> + ('three 3)
> + ('four 4)
> + ('five 5)
> + ('six 6)
> + ('seven 7)
> + ('eight 8)
> + ('nine 9)))
> +
> +(english-base-ten->number 'six)
> +@result{} 6
My previous comment still applies:
> This is a suboptimal example; this would be better done with 'case'.
> I propose replacing it with another example, or adding a note that one would normally use 'case' for this.
still applies. What is the reason for not doing something akin to that?
> +
> +(apply + (map english-base-ten->number '(one two three four)))
> +@result{} 10
> @end example
>
> -In this example, list @var{l} matches the pattern @code{('hello (who))},
> -because it is a two-element list whose first element is the symbol
> -@code{hello} and whose second element is a one-element list. Here
> -@var{who} is a pattern variable. @code{match}, the pattern matcher,
> -locally binds @var{who} to the value contained in this one-element
> -list---i.e., the symbol @code{world}. An error would be raised if
> -@var{l} did not match the pattern.
> +@page
> +@cindex pattern variable
> +@noindent Pattern matchers may contain @dfn{pattern variables},
> +local bindings to all elements that match a pattern.
'Pattern matchers' -> 'pattern' would be more precise here, as it more
precisely states _where_ the pattern variable is. E.g. if you say
'pattern', it's certainly not the 'ns' in (match ns ...). If you say
'pattern matcher' (*), then 'pattern matcher' might mean 'match' itself,
or (match ns ...); the former does not contain a pattern variable, the
latter likely does but less is stated about _where_ the pattern variable
is, purely going by your sentence it moght be the 'match' which is
incorrect.
(*) While the original text defined 'pattern matcher=match', that part
doesn't contain any pattern variables, and in your new text the notion
is of 'pattern matcher' is not exactly defined but rather described, and
not as some kind of precise characterisation.
>
> -The same object can be matched against a simpler pattern:
> +@example
> +(let re ([ns '(one two three four 9)] [total 0])
The Scheme convention would to be to write 'loop' instead of 're' when
using named-let, and something like 'rest' instead of 'ns'. The exact
word for the loop argument varies a lot, but two letters that don't
appear to mean anything are to be avoided.
> + (match ns
> + [(e) (+ total (english-base-ten->number e))]
> + [(e . es)
> + (re es (+ total (english-base-ten->number e)))]))
I tried running your example, and it doesn't work:
(define (english-base-ten->number name)
(match name
('zero 0)
('one 1)
('two 2)
('three 3)
('four 4)
('five 5)
('six 6)
('seven 7)
('eight 8)
('nine 9)))
(let re ([ns '(one two three four 9)] [total 0])
(match ns
[(e) (+ total (english-base-ten->number e))]
[(e . es)
(re es (+ total (english-base-ten->number e)))]))
ice-9/boot-9.scm:1685:16: In procedure raise-exception:
Throw to key `match-error' with args `("match" "no matching pattern" 9)'.
Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
I think you need to replace (one two three four 9) by (one two three
four nine). As you mentioned yourself (in other words), examples in the
manual should actually work as-is.
> -@example
> -(let ((l '(hello (world))))
> - (match l
> - ((x y)
> - (values x y))))
> -@result{} hello
> -@result{} (world)
> +@result{} 19
> @end example
>
> -Here pattern @code{(x y)} matches any two-element list, regardless of
> -the types of these elements. Pattern variables @var{x} and @var{y} are
> -bound to, respectively, the first and second element of @var{l}.
> -
> -Patterns can be composed, and nested. For instance, @code{...}
> +@noindent In this example, the list @code{ns} matches the pattern
> +@code{(e . es)}, where the pattern variable @code{e} corresponds
> +to the metaphoical "car" of @code{ns} and the pattern variable @code{es}
> +corresponds to the "cdr" of @code{ns}.
Typo: metaphoical -> metaphorical.
Also: metaphorical -> literal. -- e is literally the car of ns (or
‘corresponds to the car of ns in a literal way’ if you go for a
variable/value distinction); there is nothing figurative here. I would
just drop the metaphorical/literal word. Also, "car" -> `car' and "cdr"
-> `cdr' -- the manual currently consistently uses the quotation style
‘car’ / ‘pair?’, ‘SCM’, ..., not "car". For example, in 5.4.1 Dynamic
Types, there is the paragraph:
> In order to implement standard Scheme functions like ‘pair?’ and
> ‘string?’ and provide garbage collection, the representation of every
> value must contain enough information to accurately determine its type
> at run time.
'Function' -> 'Procedure'. You are introducing a stylistical
inconsistency here. In Guile, the C things are called 'Functions', and
the Scheme things are called 'Procedures'. To some degree, this ‘in
Scheme it's called a procedure’ also holds for other Schemes IIUC.
Actually, while some GC do require runtime type information (RTI), RTI
is not needed for garbage collection. Guix uses Boehm-GC for garbage
collection. Being a conservative garbage collector, it doesn't need any
type information. It works a little better if you do give it some type
information, and Guile does give it some information in some cases, but
it's not required.
This information is therefore incorrect and needs to be removed, but the
bits about predicates seems fine to me.
> Often, Scheme systems also use this information to
> determine whether a program has attempted to apply an operation to an
> inappropriately typed value (such as taking the ‘car’ of a string).
IIUC, in Texinfo, we write `stuff' instead of ‘stuff’, and it will get
turned in ‘stuff’. I dunno why this is still done in the Guile manual
as UTF-8 is an established thing, but I have used ‘’ in Guix stuff in
the past and people changed into `'.
Additionally, doing "git grep -F "car" doc/ref/*.texi", it appears that
the manual doesn't actually quote car and cdr -- instead it writes car
and cdr unquoted, or writes @code{car} / @code{cdr} which happens to be
turned into a quoted ‘car’ / ‘cdr’ in the .info documentation by Texinfo.
I think you can guess what I would be saying about stylistic consistency
here.
> +
> +@noindent A tail call @code{re} is then initiated
‘A tail call @code{re} is then initiated’ -> ‘A tail call to @code{re}
is the initiated’ -- @code{re} is a variable reference, not a tail call.
The tail call is @code{(re es (+ to total ...))}.
More simply, you could write ‘The procedure @var{re} is then tail-called’.
> +and we "cdr" down the
> +list by recurring on the tail @code{es}, applying our matcher
> +@code{english-base-ten->number} to each element of @code{ns} until
> +only a single element @code{(e)} remains, causing the @code{total}
> +to be computed. In modern Scheme programming it is common to use
> +@code{match} in place of the more verbose but familiar combination
> +of @code{cond}, @code{car} and @code{cdr}, so it's important to
> +understand how these idioms translate.
> +
> +Patterns can be composed and nested. For instance, @code{...}
> (ellipsis) means that the previous pattern may be matched zero or more
> times in a list:
> @example
> -(match lst
> - (((heads tails ...) ...)
> - heads))
> +(match '((a.0 b.0 c.0 ((1.0 2.0 3.0) x.0 y.0 z.0))
> + (a.1 b.1 c.1 ((1.1 2.1 3.1) x.1 y.1 z.1)))
> + [((heads ... ((tails ...) . rest)) ...)
> + (begin
> + (format #t "heads: ~a ~%" heads)
> + (format #t "tails: ~a ~%" tails)
> + (format #t "rest: ~a ~%" rest))])
> +@result{}
> +heads: ((a.0 b.0 c.0) (a.1 b.1 c.1))
> +tails: ((1.0 2.0 3.0) (1.1 2.1 3.1))
> +rest: ((x.0 y.0 z.0) (x.1 y.1 z.1))
> @end example
>
> -@noindent
> -This expression returns the first element of each list within @var{lst}.
> -For proper lists of proper lists, it is equivalent to @code{(map car
> -lst)}. However, it performs additional checks to make sure that
> -@var{lst} and the lists therein are proper lists, as prescribed by the
> -pattern, raising an error if they are not.
> -
> -Compared to hand-written code, pattern matching noticeably improves
> -clarity and conciseness---no need to resort to series of @code{car} and
> -@code{cdr} calls when matching lists, for instance. It also improves
> -robustness, by making sure the input @emph{completely} matches the
> -pattern---conversely, hand-written code often trades robustness for
> -conciseness. And of course, @code{match} is a macro, and the code it
> -expands to is just as efficient as equivalent hand-written code.
> -
> -The pattern matcher is defined as follows:
> +@noindent A pattern matcher can match an object against several
> +patterns and extract the elements that make it up.
> +
> +@example
> +(match '((l1 . r1) (l2 . r2) (l3 . r3))
> + [((left . right) ...)
> + (list left right)])
> +
> +@result{} ((l1 l2 l3) (r1 r2 r3))
> +@end example
> +
> +@example
> +(match '((1 . (a . b)) (2 . (c . d)) (3 . (e . f)))
> + [((key . (left . right)) ...)
> + (fold-right acons '() key right )])
> +
> +@result{} ((1 . b) (2 . d) (3 . f))
> +@end example
> +
> +@example
> +(match '(((a b c) e f g) 1 2 3)
> + [(((head ...) . rest) tails ...)
> + (acons tails head rest )])
> +
> +@result {} (((1 2 3) a b c) e f g)
> +@end example
> +
> +Patterns can represent any Scheme object: lists, strings, symbols,
> +records, etc.
> +
> +@noindent When a matching pattern is found, an expression is evaluated
> +with pattern variables bound to the corresponding elements of the object.
> +
> +@example
> +(let re ([m #(a "b" c "d" e "f" g)])
> + (match m
> + [(or (e) #(e)) e]
> + [(or #(e1 e2 es ...)
> + (e1 e2 es ...))
> + (cons (cons e1 e2)
> + (re es))]))
> +
> +@result{} ((a . "b") (c . "d") (e . "f") . g)
> +@end example
> +
> +@example
> +(let re ([m '(a b c d e f g h i)])
> + (match m
> + [(e) e]
> + [(e1 e2 es ...)
> + (acons e1 e2 (re es))]))
> +
> +@result{} ((a . b) (c . d) (e . f) (g . h) . i)
> +@end example
> +
> +@noindent Compared to hand-written code, pattern matching noticeably
> +improves clarity and conciseness---no need to resort to series of
> +@code{car} and @code{cdr} calls when matching lists, for instance.
> +It also improves robustness, by making sure the input @emph{completely}
> +matches the pattern---conversely, hand-written code often trades
> +robustness for conciseness. And of course, @code{match} is a macro,
> +and the code it expands to is just as efficient as equivalent
> +hand-written code.
> +
> +@noindent We define @code{match} as follows: @*
Why did you change this from
The pattern matcher is defined as follows:
? While the 'we' / 'our' / ... construct is pretty convenient, IMO it is
better avoided as long as the avoidance doesn't lead to awkward
constructions.
> @deffn {Scheme Syntax} match exp clause1 clause2 @dots{}
> Match object @var{exp} against the patterns in @var{clause1}
> @@ -96,9 +167,9 @@ value produced by the first matching clause. If no clause matches,
> throw an exception with key @code{match-error}.
>
> Each clause has the form @code{(pattern body1 body2 @dots{})}. Each
> -@var{pattern} must follow the syntax described below. Each body is an
> +@code{pattern} must follow the syntax described below. Each body is an
> arbitrary Scheme expression, possibly referring to pattern variables of
> -@var{pattern}.
> +@code{pattern}.
> @end deffn
>
> @c FIXME: Document other forms:
> @@ -114,7 +185,7 @@ arbitrary Scheme expression, possibly referring to pattern variables of
> @c
> @c clause ::= (pat body) | (pat => exp)
>
> -The syntax and interpretation of patterns is as follows:
> +@noindent @* The pattern language is specified as follows: @*
The stuff below still defines the interpretation, not only the
language/grammar. The change 'syntax -> language' seems fine to me, but
why remove 'interpretation'?
Additionally, I personally would go for interpretation->semantics, but
maybe that's too obscure for a general audience.
> [...]> @deffn {Scheme Syntax} match-lambda* clause1 clause2 @dots{}
> @@ -264,11 +335,10 @@ and can also be used for recursive functions which match on their
> arguments as in @code{match-lambda*}.
>
> @example
> -(match-let (((x y) (list 1 2))
> - ((a b) (list 3 4)))
> - (list a b x y))
> -@result{}
> -(3 4 1 2)
> +(match-let ([(x y ...) (list 1 2 3)]
> + [(a b ...) (list 3 4 5)])
> + (list x a y b))
> +@result{} (1 3 (2 3) (4 5))
> @end example
> @end deffn
>
> @@ -287,22 +357,34 @@ Similar to @code{match-let}, but analogously to @code{let*}, match and
> bind the variables in sequence, with preceding match variables in scope.
>
> @example
> -(match-let* (((x y) (list 1 2))
> - ((a b) (list x 4)))
> - (list a b x y))
> +(match-let* ([(x . y) (list 1 2 3)]
> + [(a . b) (list x 4 y)])
> + (list a b))
> @equiv{}
The old example was simpler and still fully demonstrated 'match-let*',
why the change (besides [])?
>[...]
>
> +@example
> +(define wrap '(((((unnest arbitrary nestings))))))
> +
> +(let unwrap ([peel wrap])
> + (match-let* ([([core ...]) peel]
> + [(wrapper ...) core])
> + (if (> (length wrapper) 1)
> + wrapper
> + (unwrap wrapper))))
> +
> +@result{} (unnest arbitrary nestings)
> +@end example
> +
(Not saying anything about this example TBC.)
Greetings,
Maxime.
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 929 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-01 16:40 ` Maxime Devos
@ 2023-02-02 17:47 ` David Pirotte
2023-02-03 10:05 ` Blake Shaw
2023-02-03 13:43 ` Josselin Poiret
0 siblings, 2 replies; 8+ messages in thread
From: David Pirotte @ 2023-02-02 17:47 UTC (permalink / raw)
To: Maxime Devos; +Cc: Blake Shaw, guile-devel
[-- Attachment #1: Type: text/plain, Size: 807 bytes --]
> > -
> > style: switch to "Indiana style", bracketing lets and clauses
> > --
> > After spending much time looking at the examples in black & white
> > to edit the texinfo document, it occurred to me just how much the
> > brackets improve legibility.
Not at all - and quite annoying. imo.
So i'd 'vote' not to use them anywhere in the guile reference manual
David.
It makes the code appear as if it was another language, and requires
you train yourself to ignore them - as they actually do _not_ have any
specific meaning, but nonetheless 'pretend they do', by their mere
presence, and this causes a reading nuisance.
They are also often used inconsistently by those who use them, as
where/how you'd use them is a matter of taste, which further adds to
the reading nuisance, imo.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-02 17:47 ` David Pirotte
@ 2023-02-03 10:05 ` Blake Shaw
2023-02-03 12:05 ` Arun Isaac
2023-02-03 13:43 ` Josselin Poiret
1 sibling, 1 reply; 8+ messages in thread
From: Blake Shaw @ 2023-02-03 10:05 UTC (permalink / raw)
To: David Pirotte; +Cc: Maxime Devos, guile-devel
David Pirotte <david@altosw.be> writes:
> Not at all - and quite annoying. imo.
> So i'd 'vote' not to use them anywhere in the guile reference manual
take the following functions in both styles:
(let unwrap ((peel '(((((unnest arbitrary nestings)))))))
(match-let* (((core ...)) peel)
((wrapper ...) core))
(if (> (length wrapper) 1)
wrapper
(unwrap wrapper))))
(let ()
(define-record-type person
(make-person name friends)
person?
(name person-name)
(friends person-friends))
(letrec ((alice (make-person "Alice" (delay (list bob))))
(bob (make-person "Bob" (delay (list alice)))))
(match alice
(($ person name (= force (($ person "Bob"))))
(list 'friend-of-bob name)))))
(let unwrap ([peel '(((((unnest arbitrary nestings)))))])
(match-let* ([([core ...]) peel]
[(wrapper ...) core])
(if (> (length wrapper) 1)
wrapper
(unwrap wrapper))))
(let ()
(define-record-type person
(make-person name friends)
person?
(name person-name)
(friends person-friends))
(letrec ([alice (make-person "Alice" (delay (list bob)))]
[bob (make-person "Bob" (delay (list alice)))]
(match alice
[($ person name (= force (($ person "Bob"))))
(list 'friend-of-bob name)])))
Without copy and pasting, which have errors, and where?
My wager is that unless you are already quite well adjusted
to lisp, its much easier to catch the errors in the "Indiana"
style examples, and if you are quite well adjusted to lisp,
you aren't really impacted by these conventions in documentation
in any concrete, meaninglful way.
> It makes the code appear as if it was another language, and requires
> you train yourself to ignore them - as they actually do _not_ have any
> specific meaning, but nonetheless 'pretend they do', by their mere
> presence, and this causes a reading nuisance.
Well, these conventions can be found throughout the gamut of scheme
literature going back to the 80s, and some of the largest scheme
projects, such as Chez, Racket, etc. employ them. So if you're
getting into Scheme, you'll necessarily encounter them, and if you
haven't been made aware that brackets are syntactic sugar for parens
in Scheme, or if that doesn't become apparent with some quick repl
experimentation, you've probably jumped into pattern matching a bit
too quickly.
But overall, it seems the objections against the Indiana style here
are primarily concerned with individual, current user/contributor
preferences, rather than out of a concern for the target audience,
which are newcomers.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-03 10:05 ` Blake Shaw
@ 2023-02-03 12:05 ` Arun Isaac
2023-02-03 13:10 ` Blake Shaw
0 siblings, 1 reply; 8+ messages in thread
From: Arun Isaac @ 2023-02-03 12:05 UTC (permalink / raw)
To: Blake Shaw, David Pirotte; +Cc: Maxime Devos, guile-devel
Hi Blake,
> Well, these conventions can be found throughout the gamut of scheme
> literature going back to the 80s, and some of the largest scheme
> projects, such as Chez, Racket, etc. employ them. So if you're
> getting into Scheme, you'll necessarily encounter them, and if you
> haven't been made aware that brackets are syntactic sugar for parens
> in Scheme, or if that doesn't become apparent with some quick repl
> experimentation, you've probably jumped into pattern matching a bit
> too quickly.
>
> But overall, it seems the objections against the Indiana style here
> are primarily concerned with individual, current user/contributor
> preferences, rather than out of a concern for the target audience,
> which are newcomers.
I don't think I agree. When I was a newcomer to guile and was reading
the sxml-match documentation in the manual for the first time, I found
it very confusing that there were square brackets. At that point, I
understood match but was confounded into thinking that sxml-match was
completely different due to the square brackets. Finally, when I
understood, I contributed a patch making everything round
parentheses. https://issues.guix.gnu.org/30920
I'd say a typical newcomer is not familiar with the gamut of scheme
literature going back to the 80s. I certainly wasn't, and still am not
to be honest.
Cheers!
Arun
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-03 12:05 ` Arun Isaac
@ 2023-02-03 13:10 ` Blake Shaw
0 siblings, 0 replies; 8+ messages in thread
From: Blake Shaw @ 2023-02-03 13:10 UTC (permalink / raw)
To: Arun Isaac; +Cc: David Pirotte, Maxime Devos, guile-devel
Arun Isaac <arunisaac@systemreboot.net> writes:
> Hi Blake,
>
>> Well, these conventions can be found throughout the gamut of scheme
>> literature going back to the 80s, and some of the largest scheme
>> projects, such as Chez, Racket, etc. employ them. So if you're
>> getting into Scheme, you'll necessarily encounter them, and if you
>> haven't been made aware that brackets are syntactic sugar for parens
>> in Scheme, or if that doesn't become apparent with some quick repl
>> experimentation, you've probably jumped into pattern matching a bit
>> too quickly.
>>
>> But overall, it seems the objections against the Indiana style here
>> are primarily concerned with individual, current user/contributor
>> preferences, rather than out of a concern for the target audience,
>> which are newcomers.
>
> I don't think I agree. When I was a newcomer to guile and was reading
> the sxml-match documentation in the manual for the first time, I found
> it very confusing that there were square brackets. At that point, I
> understood match but was confounded into thinking that sxml-match was
> completely different due to the square brackets. Finally, when I
> understood, I contributed a patch making everything round
> parentheses. https://issues.guix.gnu.org/30920
>
> I'd say a typical newcomer is not familiar with the gamut of scheme
> literature going back to the 80s. I certainly wasn't, and still am not
> to be honest.
Point taken, but I'd remark that I didn't mean that newcomers should
be familiar with existing literature, but rather that its common
convention with roots going way back, rather than an ad-hoc notation.
>
> Cheers!
> Arun
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-02 17:47 ` David Pirotte
2023-02-03 10:05 ` Blake Shaw
@ 2023-02-03 13:43 ` Josselin Poiret
2023-02-03 14:14 ` Blake Shaw
1 sibling, 1 reply; 8+ messages in thread
From: Josselin Poiret @ 2023-02-03 13:43 UTC (permalink / raw)
To: David Pirotte, Maxime Devos; +Cc: Blake Shaw, guile-devel
Hi Blake and David,
We were talking about this very node of the documentation yesterday with
Ludovic and zimoun, so here are my two cents. Rewriting this
introduction is a very good idea, the current one is pretty hard to get
into for novices.
David Pirotte <david@altosw.be> writes:
> Not at all - and quite annoying. imo.
> So i'd 'vote' not to use them anywhere in the guile reference manual
I agree that it's not "idiomatic Guile" so should probably be left out.
Regarding the examples, I think the first one is nice but the next one
is too involved, using a named let which a lot of users might not know.
I'd suggest demonstrating each feature without any extra prerequisite,
to make it as accessible as possible.
The third example, introducing the ellipsis, uses 2 of them directly,
with one nested! It also doesn't explain what the pattern variables are
bound to when an ellipsis is involved. Also, the example data you're
matching on looks too intimidating, which could scare novice readers.
Best,
--
Josselin Poiret
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] docs/match: pattern matcher example makeover
2023-02-03 13:43 ` Josselin Poiret
@ 2023-02-03 14:14 ` Blake Shaw
0 siblings, 0 replies; 8+ messages in thread
From: Blake Shaw @ 2023-02-03 14:14 UTC (permalink / raw)
To: Josselin Poiret; +Cc: David Pirotte, Maxime Devos, guile-devel
Josselin Poiret <dev@jpoiret.xyz> writes:
> Hi Blake and David,
>
> We were talking about this very node of the documentation yesterday with
> Ludovic and zimoun, so here are my two cents. Rewriting this
> introduction is a very good idea, the current one is pretty hard to get
> into for novices.
>
> David Pirotte <david@altosw.be> writes:
>
>> Not at all - and quite annoying. imo.
>> So i'd 'vote' not to use them anywhere in the guile reference manual
>
> I agree that it's not "idiomatic Guile" so should probably be left out.
To avoid bikeshedding, I've reverted back to not use brackets.
>
> Regarding the examples, I think the first one is nice but the next one
> is too involved, using a named let which a lot of users might not know.
> I'd suggest demonstrating each feature without any extra prerequisite,
> to make it as accessible as possible.
>
I agree that it escalates quickly. I was hoping to juxtapose the obvious
with whats more involved in hopes of hitting a "sweet spot" where the
total novice can go through the examples one-by-one and still comprehend
them, while an experienced functional programmer without Scheme/Guile
experience can jump straight in, review a few examples, catch the drift
and keep working on what they are working on, without much reading.
I wanted to go straight from an arbitrary example to one with recursion
in order to show how to think "heads/tails" recursively with a
Scheme matcher, because I personally found it initially intimidating to
start doing recursive pattern matching in Scheme, and found few examples
that do so in a highly simplified form, making it confusing to translate
knowlege from other functional languages.
From my POV, named let seems like a pretty standard feature of Scheme,
and its used throughout the docs (perhaps that isn't a good thing).
What if I followed it with an equivalent conditional version, and
used the more explicit "recur" or "lp" or "loop" as the let's name?
smth like:
...
≣
(let recur ((ns '(one two three four nine))
(total 0))
(if (equal? 1 (length ns))
(+ total (english-base-ten->number (car ns)))
(recur (cdr ns)
(+ total (english-base-ten->number (car ns))))))
> The third example, introducing the ellipsis, uses 2 of them directly,
> with one nested! It also doesn't explain what the pattern variables are
> bound to when an ellipsis is involved.
just before the third example we have:
--
`Patterns can be composed and nested. For instance, @code{...}
(ellipsis) means that the previous pattern may be matched zero or more
times in a list:`
--
How would you improve on this?
Also it does explain what the pattern variables are bound to, but as the
result rather than in writing, which I think makes for more digestible
"reference" material.
> Also, the example data you're
> matching on looks too intimidating, which could scare novice readers.
I think this one may look complicated from afar, but I added it to [V2]
because it explicates something that can't be demonstrated from simpler
data and I think is often missed: that ellipsis allow pattern variables
to bind every for every instance of a pattern. I chose to show this
immediately, followed by simpler examples, so that the reader is exposed
this idea upfront.
take a simpler example:
(match '(a b (1 2 3 ))
((heads ... (tails ...))
(format #t "heads: ~a ~%tails: ~a ~%" heads tails)))
⇒
heads: (a b)
tails: (1 2 3)
Psychologically, it is easy for the reader to not pickup on
the fact that ellipsis allow you to match over nested patterns.
This may cause readers to resort to using match with map when
its unneccessary.
The below contains two lists, (a.n b.n ((1.n 2.n 3.n) x.n y.n z.n)),
where the the letters and numbers follow an obvious order, and the
results show the effect of nesting patterns.
(match '((a.0 b.0 c.0 ((1.0 2.0 3.0) x.0 y.0 z.0))
(a.1 b.1 c.1 ((1.1 2.1 3.1) x.1 y.1 z.1)))
(((heads ... ((tails ...) . rest)) ...)
(begin
(format #t "heads: ~a ~%" heads)
(format #t "tails: ~a ~%" tails)
(format #t "rest: ~a ~%" rest))))
⇒
heads: ((a.0 b.0 c.0) (a.1 b.1 c.1))
tails: ((1.0 2.0 3.0) (1.1 2.1 3.1))
rest: ((x.0 y.0 z.0) (x.1 y.1 z.1))
I imagine the element names could be improved, and perhaps
the data structure simplified while preserving the lesson that
it contains, but I can't think of how that would be done, but
I'm open to suggestions. But overall, I think showing nested
patterns first off, followed by simpler examples that
ellucidate previous ones, is preferable so as to cater both
to the "reference" user as well as the total beginner.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2023-02-03 14:14 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-01 13:09 [PATCH v3] docs/match: pattern matcher example makeover Blake Shaw
2023-02-01 16:40 ` Maxime Devos
2023-02-02 17:47 ` David Pirotte
2023-02-03 10:05 ` Blake Shaw
2023-02-03 12:05 ` Arun Isaac
2023-02-03 13:10 ` Blake Shaw
2023-02-03 13:43 ` Josselin Poiret
2023-02-03 14:14 ` Blake Shaw
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).