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

First-class lambdas #4005

Open
Bilge opened this issue Mar 4, 2024 · 1 comment
Open

First-class lambdas #4005

Bilge opened this issue Mar 4, 2024 · 1 comment

Comments

@Bilge
Copy link

Bilge commented Mar 4, 2024

It is noted that when calling functions/macros specifically, one can use lambdas as discussed here. However, it is not possible to assign a lambda to a variable or a hash literal. Therefore, it is not possible to pass a lambda from one template to another. However, I would like to propose first-class lambda support is possible in Twig.

To establish why this may be necessary, consider a template that contains a loop. We want to replace some content within the loop from another template. Since it is not possible to pass lambdas, we cannot do this.

{# app_list.twig #}
{% for app in apps %}
{% block score %}{{ (app.score * 10)|number_format(2) }}{% endblock %}
{% endfor %}
{# my.twig #}
{% extends 'app_list.twig' %}

{# This makes no sense because `app` is either undefined in this context or refers to something else;
it does not refer to each entry in `apps` as in app_list.twig. #}
{% block score %}{{ (app.score * 100)|number_format(0) }}{% endblock %}

First-class lambdas would be one way to solve this problem.

{# app_list.twig #}
{% for app in apps %}
{{ scorer(app)|default((app.score * 10)|number_format(2)) }}
{% endfor %}
{# my.twig #}
{{ include('app_list.twig', {
  scorer: app => (app.score * 100)|number_format(0),
}) }}

Note that we had to change our reuse strategy from inheritance (extends) to include, since there doesn't seem to be a logical way to pass a lambda to a block.

Whilst one could make the case for supplying app.score already formatted from the backend, this will not always be possible or ideal architecture, since the knowledge of which formatting to use may depend on which specific combination of templates is used, which is only known by the front-end. It should also be considered impractical to create twig extension functions for every possible permutation of score formatting.

@stof
Copy link
Member

stof commented Apr 19, 2024

this would require removing the validation of known functions at compile time by replacing any unknown function with a runtime call to a lambda, because there would be no way to know at compile time whether scorer(app) is meant to be a call to a lambda or to a Twig function.
And this also leads to questions about precedence between variable names containing lambdas (known only at runtime) and Twig function names in case you have both names. Reusing the scorer() syntax to call lambdas means that we don't have separate naming spaces anymore for functions and variables and we need to define priority. And any priority would be confusing IMO:

  • preferring Twig functions means that any Twig extension can break your template using a lambda if it defines a scorer function
  • preferring lambdas would add overhead for all function calls in Twig by requiring a runtime check before calling the Twig function. And it could be a BC break in case you already have variables using the name of a Twig function and you expect to call the Twig function (as this is what happens today).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants