In favor of explicit argument Shiro Kawai (09 Aug 2020 01:33 UTC)
Re: In favor of explicit argument Lassi Kortela (09 Aug 2020 06:46 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (09 Aug 2020 09:27 UTC)
Re: In favor of explicit argument Adam Nelson (10 Aug 2020 22:25 UTC)
Re: In favor of explicit argument Shiro Kawai (10 Aug 2020 23:46 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (11 Aug 2020 07:58 UTC)
Re: In favor of explicit argument Alex Shinn (11 Aug 2020 01:29 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (11 Aug 2020 07:17 UTC)
Re: In favor of explicit argument Jim Rees (11 Aug 2020 16:45 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (11 Aug 2020 16:57 UTC)
Re: In favor of explicit argument Alex Shinn (12 Aug 2020 02:20 UTC)
Re: In favor of explicit argument John Cowan (12 Aug 2020 02:49 UTC)
Re: In favor of explicit argument Arthur A. Gleckler (12 Aug 2020 03:23 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (12 Aug 2020 13:29 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (12 Aug 2020 19:46 UTC)
Re: In favor of explicit argument Alex Shinn (13 Aug 2020 00:40 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (13 Aug 2020 07:18 UTC)
Re: In favor of explicit argument Alex Shinn (14 Aug 2020 01:24 UTC)
Re: In favor of explicit argument Adam Nelson (13 Aug 2020 01:13 UTC)
Re: In favor of explicit argument John Cowan (13 Aug 2020 01:53 UTC)
Re: In favor of explicit argument Adam Nelson (13 Aug 2020 03:09 UTC)
Re: In favor of explicit argument Alex Shinn (13 Aug 2020 03:16 UTC)
Re: In favor of explicit argument John Cowan (13 Aug 2020 03:31 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (13 Aug 2020 08:04 UTC)
Re: In favor of explicit argument Jim Rees (13 Aug 2020 18:24 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (13 Aug 2020 20:05 UTC)
Re: In favor of explicit argument John Cowan (14 Aug 2020 02:41 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (14 Aug 2020 06:34 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (14 Aug 2020 13:30 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (14 Aug 2020 14:08 UTC)
Re: In favor of explicit argument Alex Shinn (15 Aug 2020 22:56 UTC)
Re: In favor of explicit argument Marc Nieper-Wißkirchen (16 Aug 2020 07:55 UTC)
Re: In favor of explicit argument Alex Shinn (14 Aug 2020 02:29 UTC)

Re: In favor of explicit argument Marc Nieper-Wißkirchen 11 Aug 2020 07:57 UTC

Am Di., 11. Aug. 2020 um 00:25 Uhr schrieb Adam Nelson <xxxxxx@nels.onl>:
>
> I'm disappointed that there's such widespread agreement on a change that, in my opinion, defeats the original purpose of this SRFI. This change removes any possibility of brevity from the `chain` operator. It's now always longer than the equivalent nested expression.

The number of characters is a bad measure of brevity or simplicity, in
particular for Scheme, which is rather verbose. The nesting level
(indentation and/or parentheses nesting level) on the other hand, is a
better measure. And with respect to this, even with an explicit "_"
(shorter than "<>"), this SRFI is very helpful.

We can write all kinds of macros for simplifying our programming tasks
and these are only limited by the power of "define-syntax". For macros
that shall make it into a future Scheme standard, however, consistency
and readability are a very important factor. With an implicit "_", the
chain macro is inconsistent when it comes to multiple values because
one, two, three, ... "_"s mean something different than zero "_"s.
Shiro has already talked about readability.

In a discussion about SRFI 200 Shiro has also made about point that
such consistency can be important for macros generating macros (that
discussion was about the balancing of quasiquotes and unquotes but it
equally applies to irregular edge cases).

The inconsistency of the special casing of zero placeholders is
particularly leaping to the eye in the first example given for
"chain-lambda":

(chain-lambda (a) (b)) ; => (lambda (_) (b (a _)))

The explicit version allowed by this SRFI would be (using "_"):

(chain-lambda (a _) (b_)) ;=> (lambda (_) (b (a _)))

Now given that, if we drop the first "_", one would expect the regular version:

(chain-lambda (a) (b_)) ;=> (lambda () (b (a)))

However, SRFI 197 in its current state does not respect this expectancy.

> I've been comparing pipelines written in both styles, trying to figure out if this is just me being overly attached to how I've already written my code, or if there's a real disadvantage to the added verbosity. Here's a one-liner written in Draft 1 style and this new style:
>
>     (->> x reverse car number->string (string-ref 0) char->integer)
>
>     (chain x (reverse <>) (car <>) (number->string <>) (string-ref 0 <>) (char->integer <>))

> The new one doesn't look bad; you could argue that it's more readable. But, imo, an important difference is that the first one could be a one-liner inside a more complex expression, while the second one seems too long for that. (Arguably the extra parentheses also make it look less like a pipeline at a glance, but that decision has already been made.)

With the explicit "_" it would be

(chain x (reverse _) (car _) (number->string _) (string-ref 0 _)
(char->integer _))

I don't think it is too bad.

> `chain-last` is a compromise, but it adds extra characters to the less-verbose option, and it's not clear what the naming convention should be for the `-last` versions of the other `chain` operators. (maybe use `/last` instead?)

What about "chain*"? Or "chain_"? Then, we can also have a "*chain" or
"_chain" version for an implicit first argument. If such supplementary
macros are added, I would suggest to specify them only for single
values so the explicit versions are used for multiple values.

> Something else I'm worried about is dangling list arguments after lambdas. This is one of my most common use cases for `chain`:
>
>     (chain x
>       (map (lambda (y)
>              (if (foo y)
>                  (bar y)
>                  (baz (qux y)))))
>       (map (lambda (z)
>              (if (foo z)
>                  (bar z)
>                  (baz (qux z))))))
>
>
> List operations take the list as the last argument, which matches `chain`'s default argument position. If the default argument weren't supported, each operation would need an extra line for a dangling `<>`:
>
>     (chain x
>       (map (lambda (y)
>              (if (foo y)
>                  (bar y)
>                  (baz (qux y))))
>            <>)
>       (map (lambda (z)
>              (if (foo z)
>                  (bar z)
>                  (baz (qux z))))
>            <>))

Actually, it took me a moment to mentally digest the first version
while the second version is just perfectly readable (even if one
hasn't read the spec for SRFI 197 in detail). We have to keep those
people in mind who read code written with "chain" but haven't known
"chain" before.

> This is more pronounced when the lambdas are huge and the `<>` may be a dozen indent and nesting levels down, and it's easy to put it in the wrong place or omit it. Sometimes I use `chain` for a single `map` operation, just to write it in this style without the dangling argument.
>
> And my most petty/personal objection is this: Out of all of the utility macros I've written, these chaining macros are my second most used (next to pattern matching). My code is full of them, but the required trailing `<>` might be the last straw that causes me to not bother keeping compatibility with this SRFI and to go back to the original `->` style in my own code. I would really like to avoid doing that.

That's why I wrote above that "define-syntax" encourages us to create
all kinds of abbreviations and new syntaxes for our own code and also
for very specialized DSLs. When it comes to standardization, however,
other factors have to necessarily come in and some syntaxes have to be
smoothed out.

> I'm aware that my objections to this change are petty. If I can't change anyone's mind on this, then I'll still make the change; SRFIs should be a democratic process, and I don't want to decide on something that irritates the wider Scheme community based only on my aesthetic preferences. But this macro is, ultimately, an aesthetic feature, and every spurious character can become a wart that makes Scheme that much more unappealing to newcomers familiar with other languages.

So wouldn't the "_" character be a good compromise? It is shorter than
"<>" and visually much more appealing. The code is easy to read and
there is no conceptional wart of zero "_"'s behaving differently than
one or more "_"s.

And special "chain*" and "*chain" versions for single-valued pipelines?

Marc

>
> On 8/9/20 5:26 AM, Marc Nieper-Wißkirchen wrote:
>
> I can only agree with you here. The code does not only become easier
> to read, it will also eliminate an irregularity, which is always good:
> Without any exceptional rule, no "<>" would read as "zero values",
> which, while probably not used very often, would be the obvious
> meaning.
>
> The regular version with no values has some uses for thunks.
>
> (chain-lambda (a) (b <>))
>
> would translate into the thunk (lambda () (b (a))).
>
> (If one really thinks that a macro with an implicit "<>" is needed,
> such a macro should be some supplementary macro, e.g. chain-last, and
> not one of the central macros in this SRFI.)
>
> Am So., 9. Aug. 2020 um 08:46 Uhr schrieb Lassi Kortela <xxxxxx@lassi.io>:
>
> In Clojure I use threading macros often, but one thing that always
> bothers me is that the argument position is implicit (for most of the
> family of macros).  It is ok while working on it, but once I leave the
> coding for a while and then look at the code, it's extremely confusing.
>
> +1
>
> I ended up always marking the inserted argument position with commas,
> which are treated as whitespaces in Clojure.
>
> srfi-197 allows to use <> as the insertion points, and I can always use
> it even the argument position is the last position:
>
>    (chain (a b) (c d <>) (e f <>))
>
> This is a nice notation.
>
> I've always found the <> marker a bit confusing since it stands for "not
> equal" in Pascal and many other languages. But it's inherited from SRFI
> 26 `cut` and `cute` which I guess set a strong precedent. Starting from
> scratch, I would prefer underscore which is a placeholder marker in many
> languages:
>
> (chain (a b) (c d _) (e f _))
>
> And I wonder if we gain anything by allowing <> in the last position to
> be omitted.
>
> Sure it may shorten the code a bit.  However, the spec is effectively
> the same as  "show the argument position with <> except when it's the
> last position, in which case you can omit it."    It looks somewhat an
> arbitrary and irregular rule.
>
> +1
>
> Code is read far more times than written.  Saving a few keystrokes
> trading off readability doesn't seem a good deal.
>
> Is everyone comfortable with implicit argument?  Certainly, when I see
> (cons "x"), I can see there's an implicit argument, since the fact that
> cons takes two arguments is engraved in my brain (the same circuit fires
> when I'm reading Haskell code).
> But it isn't always necessary the case, e.g. (list 'a 'b)
>
> Good point.