doc icon indicating copy to clipboard operation
doc copied to clipboard

Replace incorrect use of "currying" with "priming"

Open codesections opened this issue 1 year ago • 15 comments

The documentation currently uses the term "currying" to refer to what is more broadly known as "partial function application" (but frequently incorrectly called "currying"). However, Raku generally doesn't use the phrase "partial function application", probably because it's a real mouthful and could be off-putting to people not familiar with functional programming jargon. Instead, Raku prefers the term "priming", see S06.

Accordingly, this PR changes (nearly) all uses of "currying" or related words to the "priming" equivalent; it also adds a "priming" entry to the glossary.

The only two (intentional) uses of "currying" left are in a TODO section of the Haskell-to-Raku guide and in the documentation for the (Rakudo-specific, unspecified) type CurriedRoleHOW.

See related discussion with @raiph in the comments to this StackOverflow answer.

codesections avatar Nov 09 '23 02:11 codesections

I think the aim is noble but do you think this is enough? I mean, we have been using the term "whatever-currying" somewhat regularly for years now. How many involved people know that we are practically breaking away from that terminology?

2colours avatar Nov 09 '23 11:11 2colours

I don't think this applies to whatever-currying, but more to what .assuming does?

lizmat avatar Nov 09 '23 11:11 lizmat

I support changing the documentation from "currying" to "priming". The change makes the documentation more correct, and just because some people were accustomed to using words in the wrong way before, that doesn't mean they need to keep doing so going forward. Using more standard terms will also make it easier for the wider world to come to Raku. And a key thing is that it isn't changing actual language keywords so it isn't going to break code.

I will also say I'm surprised this wasn't already done almost a decade ago, as I distinctly recall being in a discussion about this very matter then. I have my own other language heavily influenced by Raku, and I used the term "currying" originally to refer to the partial function application, following Raku's example, and then the discussion convinced me to refer to it as "priming" instead. I had thought Raku was making the same change at that time.

duncand avatar Nov 09 '23 12:11 duncand

@lizmat wrote:

I don't think this applies to whatever-currying, but more to what .assuming does?

But the point is that whatever-priming/currying does exactly the same thing as .assuming. Here are functionally equivalent lines of code:

my &add1-v1 = 1 + *;
my &add1-v2 = &[+].assuming(1);

my &find-primes-v1 = *.grep(&is-prime);
my &find-primes-v2 = List.^lookup('grep').assuming(*, &is-prime);

And since using * achieves the same result as using .assuming, it makes sense to call them both "priming".

I didn't see this until now, but S06 makes this point explicitly:

Perl 6 has several ways of performing partial function application. Since this is an unwieldy term, we've settled on calling it priming. (Many folks call this "currying", but that's not really a correct technical usage of the term.) Most generally, priming is performed on a Callable object by calling its .assuming method, described elsewhere. This section is about a convenient syntactic sugar for that.

For any unary or binary operator (specifically, any prefix, postfix, and infix operator), if the operator has not specifically requested (via signature matching) to handle * itself, the compiler is required to translate directly to an appropriately primed closure at compile time. We call this autopriming.

Maybe ^^^^ is what @duncand is remembering being settled, and the docs just never got updated?

codesections avatar Nov 09 '23 17:11 codesections

Here are functionally equivalent lines of code: my &add1-v1 = 1 + *; my &add1-v2 = &[+].assuming(1);

Except that the first creates a Callable out of thin air, and the second one creates a Callable by wrapping an existing Callable. I can see the second one be called "priming". But calling the first one "priming" as well, feels wrong to me.

For instance, what would be the "assuming" version of *.frobnicate ?

lizmat avatar Nov 09 '23 17:11 lizmat

Except that the first creates a Callable out of thin air

No it doesn't. Both create a Callable by wrapping &infix:<+>, just with different spelling.

For instance, what would be the "assuming" version of *.frobnicate ?

That's just my second example but without assuming any args. So it'd be:

my &frobnicate = WhateverTypeHasFrobnicateMethod.^lookup('frobnicate');
# or, if you prefer to _explicitly_ assume nothing:
my &frobnicate2 = WhateverTypeHasFrobnicateMethod.^lookup('frobnicate').assuming();

(But all that does is let you call frobnicate with subroutine syntax, so you might as well have just used frobnicate $arg:;)

codesections avatar Nov 09 '23 17:11 codesections

Except that method calls are late bound, so that won't fly.

In any case, to me there is a difference between creating a Callable out of thin air (which I think the syntax *.frobnicate is doing), and calling .assuming on an existing Callable.

lizmat avatar Nov 09 '23 17:11 lizmat

Except that method calls are late bound, so that won't fly.

I see what you mean; Str.^lookup('tc').assuming() always calls the Str method, whereas *.tc will call the first tc method in the mro of whatever it's called on.

So here's how I'd describe that difference: &assuming is a function that takes a function and 0 or more arguments; it returns a function that calls the original function with the both the arguments passed to &assuming and the arguments passed to the inner function.

Or, in code a simplified version (e.g., no named args) of &assuming would be:

sub assuming(&code, *@args) {
    -> *@rest { code(|@args, |@rest) }
}

Whereas *, when used before a method call, is a function (er, syntax that is equivalent to a function) that takes a method-name and zero or more arguments. It returns a function that takes 1 or more arguments; this inner function calls the method named in the outer function with the arguments passed to * and the arguments passed to the inner function.

Or in code (again, simplified and this time also ignoring * when used as an operand).:

multi whatever(Str $method-name, *@args) {
    -> $invocant,  *@rest { $invocant."$method-name"(|@args, |@rest)}
}

So, I now agree with @lizmat that these are two (subtly) different things. And that &assuming meets the strict definition of "partial function application" whereas * does not – even though * does perform partial application, it does something more.

But even if what * does isn't quite partial application, it's certainly not currying. And I agree with S06 that it makes sense to call what * does "priming" – which, after all, doesn't have to be 100% the same as a strict definition of partial function application. For example, when we have the expression *.grep(&so) it makes sense to say that we've primed the grep method with &so. More generally, I'd say that – though your point about late-binding is important – what &assuming and * do is similar enough that we ought to use the same terminology when discussing them.

codesections avatar Nov 09 '23 20:11 codesections

@codesections wrote:

I didn't see this until now, but S06 makes this point explicitly:

Perl 6 has several ways of performing partial function application. Since this is an unwieldy term, we've settled on calling it priming. (Many folks call this "currying", but that's not really a correct technical usage of the term.) Most generally, priming is performed on a Callable object by calling its .assuming method, described elsewhere. This section is about a convenient syntactic sugar for that. For any unary or binary operator (specifically, any prefix, postfix, and infix operator), if the operator has not specifically requested (via signature matching) to handle * itself, the compiler is required to translate directly to an appropriately primed closure at compile time. We call this autopriming.

Maybe ^^^^ is what @duncand is remembering being settled, and the docs just never got updated?

Yes, thank you!

I should also clarify that I don't necessarily support the actual changes of this specific pull request, but rather the principle of using "priming" rather than "currying" where it is the more clearly correct meaning. Before it seemed like this was recognized but not completely followed through on years ago, like you found some vestigial uses of the wrong term. But now I recognize it is possible that what you found is some cases where "currying" is referring to something else. But I don't know enough to judge so I'm neutral on the current PR itself.

On a different subject matter I noticed it seems GitHub allows me to edit other people's comments, which is odd, I would have thought only the author of a comment would be allowed to edit it.

duncand avatar Nov 09 '23 21:11 duncand

Just a heads up that in RakuAST there is CurryThunk. This could be changed to PrimeThunk without any issue.

At first I was against this change but the more I think about it the more I think that "priming" something is a lot more evocative to me than "currying" something is, even though the latter still feels more familiar.

ab5tract avatar Nov 10 '23 17:11 ab5tract

My point is specifically that it might cause confusion if this change happens without people really knowing about it, and that in general it almost feels more important to inform people about it than what the doc says. If the doc just "contradicts" what people say and nothing reflects on that, I think that's almost automatically bad PR ("they are indecisive", "look how many of them use plain wrong terminology" etc.). Just think of the "path to Raku" topic; it would be great to do better than that, regarding clear communication and expressing some sort of expectations to people who want to help anyway.

2colours avatar Nov 10 '23 19:11 2colours

@2colours wrote:

My point is specifically that it might cause confusion if this change happens without people really knowing about it, and that in general it almost feels more important to inform people about it than what the doc says. If the doc just "contradicts" what people say and nothing reflects on that,

Those all seem like fair points. Do you have a suggestion for what we could/should do to inform people/reflect on the change? The only thing that's coming to mind for me is one of us writing a personal blog post, but I'm not sure if that would address your concerns or not?

(The "you" ^^^ can be singular or plural; I'd of course welcome ideas from anyone about how to communicate re: doc terminology. AFAIK, we don't currently have a process for that but I can see how it'd be useful.)

codesections avatar Nov 11 '23 21:11 codesections

I think as far as the existing patterns go, writing, if only a short article about the rationale, and "circulating" it in the weekly/mastodon/reddit/IRC is mostly good. I can't immediately think of a better way now.

2colours avatar Nov 11 '23 23:11 2colours

See ticket #3796

coke avatar Nov 17 '23 16:11 coke

I am in general in favor of this change; can we address existing comments and can someone summarize what's left to decide?

coke avatar Nov 18 '23 03:11 coke

@codesections, @lizmat - is there anything left to address in this PR or can I apply it?

coke avatar May 17 '24 17:05 coke

@coke,

I'm with codesections (and Larry) that the word "Currying" is a poor choice (because what it refers to is not currying), and the word "Priming" is a good choice (because it's a good choice of English word that describes what it's doing), and thus the "Currying" heading @silentTeee spotted above should be changed to "Priming".

raiph avatar May 17 '24 21:05 raiph

Did silentTree's as a separate followup commit. Thanks everyone.

coke avatar May 18 '24 01:05 coke