unofficial mirror of bug-guile@gnu.org 
 help / color / mirror / Atom feed
From: Mathieu Lirzin <mthl@gnu.org>
To: Mark H Weaver <mhw@netris.org>
Cc: 30237@debbugs.gnu.org
Subject: bug#30237: Generalizing ‘and=>’
Date: Wed, 31 Jan 2018 15:23:43 +0100	[thread overview]
Message-ID: <87shamm7io.fsf@gnu.org> (raw)
In-Reply-To: <87shamvebr.fsf@netris.org> (Mark H. Weaver's message of "Tue, 30 Jan 2018 23:31:52 -0500")

Mark H Weaver <mhw@netris.org> writes:

> This generalization will inevitably slow it down, and it's not clear
> that this is useful in practice.  It's also not particularly natural,
> because these return value(s) somehow need to be interpreted as a
> boolean.  There are multiple ways to do that, and it's not clear which
> is the best way.
>
> I think we should resist the temptation to make simple things more
> complicated without good reason.  Making things more complicated always
> has a cost.  I'm a big believer in keeping things simple, especially the
> widely used primitive operations.

Generalizing the arity of things tends to make them more “simple” for
example thinking of ‘+’ as an arbitrary arity function instead of a
binary operator is simpler.

I am realizing that In this case, the semantics are indeed unclear for
multiple values.  For example if multiple values were to be handled then
‘(and=> (values 1 2) list)’ should work in the first place.  Moreover as
you pointed what should be done regarding the truthiness of multiple
values is not clear either.  As a consequence I withdraw my proposition
to generalize ‘and=>’ to multiple values.

Having said that I think the implementation you previously proposed
(without the arity 0 case) keep things simple:

  (define and=>
    (case-lambda
      ((val proc) (and val (proc val)))
      ((val . procs)
       (let loop ((val val) (procs procs))
         (if (null? procs)
             val
             (and val (loop ((car procs) val)
                            (cdr procs))))))))

> Finally, there still some question in my mind whether this
> generalization would be useful in practice.  Have you found a
> real-world use case where this generalized 'and=>' makes life easier?

I took some time to think about a pseudo-realistic use case.  Consider
some configuration variables that are set from the process environment.
We want to check and transform what the user provides with some slight
variations for each variable:

  ;;; Higher-order utilities.

  (define (ensure pred)
    (lambda (val)
      (and (pred val) val)))

  (define (check pred msg)
    (lambda (val)
      (unless (pred val)
        (display msg))
      val))

  ;;; Process and check variables (unrealistic but give an idea).

  (define (split-path str)
    (string-split str #\:))

  (define (no-empty-strings? lst)
    (not (any (lambda (str) (string=? "" str)) lst)))

  (define (keep-existing-files lst)
    (filter file-exists? lst))

  (define (warn-deprecated-path lst)
    (when (any (lambda (string-suffix? "/old-bar")))
       (display "Please don't refer to \"old-bar\" for XYZ reason")))

  ;;; Global variables.

  (define %foo
    ;; Apply procedures in a pipeline fashion...
    (and=> (and=> (and=> (and=> (getenv "FOO") split-path)
                         (ensure no-empty-strings?))
                  keep-existing-files)
           (ensure pair?)))

  (define %bar
    ;; ...or with more idiomatic procedure calls.
    (let ((lst (split-path (or (getenv "BAR")
                               "/etc/bar:/usr/share/bar"))))
      (when (no-empty-strings? lst)
        (warn-deprecated-path lst)
        lst)))

Handling arbitrary arities would allow rewriting those variables in a
more elegant way:

  (define %foo
    (and=> (getenv "FOO")
           split-path
           (ensure no-empty-strings?)
           keep-existing-files
           (ensure pair?)))

  (define (no-deprecated-dirs? lst)
    (not (any (lambda (str) (string-suffix? "/old-bar" str)) lst)))

  (define %bar
    (and=> (or (getenv "BAR") "/etc/bar:/usr/share/bar")
           split-path
           (ensure no-empty-strings?)
           (check no-deprecated-dirs?
                  "Please don't refer to \"old-bar\" for XYZ reason")))

‘and-let*’ would be a reasonable alternative but the benefit of this
form is that it favours the use of higher-order procedures in place of
special syntax.

WDYT?

-- 
Mathieu Lirzin
GPG: F2A3 8D7E EB2B 6640 5761  070D 0ADE E100 9460 4D37





      reply	other threads:[~2018-01-31 14:23 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-24 12:10 bug#30237: Generalizing ‘and=>’ Mathieu Lirzin
2018-01-24 15:01 ` Mark H Weaver
2018-01-24 20:08   ` Mathieu Lirzin
2018-01-31  4:31     ` Mark H Weaver
2018-01-31 14:23       ` Mathieu Lirzin [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87shamm7io.fsf@gnu.org \
    --to=mthl@gnu.org \
    --cc=30237@debbugs.gnu.org \
    --cc=mhw@netris.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).