unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* [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).