Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should we support ((f)) syntax for higher order functions? #145

Open
countvajhula opened this issue Jan 5, 2024 · 2 comments
Open

Should we support ((f)) syntax for higher order functions? #145

countvajhula opened this issue Jan 5, 2024 · 2 comments
Labels
RFC Comments from the community are solicited

Comments

@countvajhula
Copy link
Collaborator

countvajhula commented Jan 5, 2024

The docs say this:

If you have a function that returns another function that you’d like to use as a flow (e.g. perhaps parametrized by the first function over some argument), the usual way to do it is something like this:

(~> (3) (esc (get-f 1)))

But in an idle moment, this clever shortcut may tempt you:

(~> (3) ((get-f 1)))

That is, since Qi typically interprets parenthesized expressions as partial application templates, you might expect that this would pass the value 3 to the function resulting from (get-f 1). In fact, that isn’t what happens, and an error is raised instead. As there is only one datum within the outer pair of parentheses in ((get-f 1)), the usual interpretation as partial application would not be useful, and could even lead to unexpected behavior (at least, with the current implementation that uses Racket’s curry).

Now that we no longer use Racket's curry to implement this (we use a simple lambda instead), there would no longer be "unexpected behavior" here (IIRC the unexpected behavior was that a curried function applied to zero arguments expects to be applied at least one more time to pre-supplied arguments before actually evaluating to a result on the third invocation). Our partial application syntax matches (name arg ...+). We could change this to (name arg ...) and that would then expect an expression like (e), where e is any Racket expression, to evaluate to a partial application of a flow arising from e to no arguments. So although it could be a more convenient syntax for using higher-order functions where it's conceivable one might prefer ((f)) to (esc (f)), it would effectively be an alternative way to say (esc e) always, for any Racket expression e producing a flow, and that could potentially be confusing. Technically, a partial application form expands to a use of a blanket template (i.e. (e) becomes (e __), whereas an esc form compiles directly to the escaped expression. These may always be equivalent in practice, though. Or it may be that this would suffer from the issue identified later in that section of the docs:

One way to dodge this is by using an explicit template:

(~> (3) ((get-f 1) _))

This works in most cases, but it has different semantics than the version using esc, as that version evaluates the escaped expression first to yield the flow that will be applied to inputs, while this one only evaluates the (up to that point, incomplete) expression when it is actually invoked with arguments. In the most common cases there will be no difference to the result, but if the flow is invoked multiple times (for instance, if it were first defined as (define-flow my-flow ( ((get-f 1) _)))), then the expression too would be evaluated multiple times, producing different functions each time. This may be computationally more expensive than using esc, and also, if either get-f or the function it produces is stateful in any way (for instance, if it is a closure or if there is any randomness involved), then this version would also produce different results than the esc version.

Should we allow this?

@countvajhula countvajhula added the RFC Comments from the community are solicited label Jan 5, 2024
@benknoble
Copy link
Collaborator

I'm assuming this won't affect the currently planned release ? This seems like the kind of thing that may have lots of hard-to-catch effects in extant code, but I confess I don't fully understand it yet.

@countvajhula
Copy link
Collaborator Author

Yes, agreed. I'm not sure of its consequences yet. It would be something to consider for a future release rather than the upcoming one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC Comments from the community are solicited
Projects
None yet
Development

No branches or pull requests

2 participants