Skip to content

Lambda expressions in Liquid #15

@jg-rp

Description

@jg-rp

Historically, standard where and map filters (and others) are limited to selecting and comparing properties at the root of an object (each item in an array/sequence). Instead of passing strings as arguments to these filters, we want an alternative syntax that:

  • allows for selecting object/mapping/hash/dict properties and array/sequence/list items with arbitrary nesting
  • is unambiguous at template parse time
  • supports all comparison operators (!=, ==, >=, >, etc..)
  • supports all logical operators (not, and, or)
  • supports membership operators (in, contains)
  • will be easier to apply syntax highlighting

Projects depending on Python Liquid2 can of course opt to use "standard" filters following Shopify/Liquid syntax and behaviour.

Lambda expressions

In Liquid2, a lambda expression takes the form <identifier> => <boolean expression> or (<identifier>, <identifier>) => <boolean expression>, like i => i.foo.bar or (i, j) => i.foo != x.bar. The right hand side of a lambda expressions follows the same syntax as expressions found in the {% if %} tag.

Boolean expressions are terminated by a comma (,), a pipe (|), whitespace control (-, + or ~) or a closing tag/output delimiter (%} or }}).

Lambda expressions are only allowed as arguments to some filters. It is an error to pass a lambda expression to a filter that does not accept such arguments.

Some filters will be limited to accepting lambda expressions that are a single path (to a variable). With the map filter, for example, it would not make sense to evaluate a logical expression. For this reason we introduce parse-time filter argument validation, giving filter implementations the option to raise an exception when a template is parsed instead of waiting until it is rendered.

We've opted for => instead of -> because we assume template authors will be more familiar with JavaScript arrow functions than they will be with Ruby lambda literals.

Usage examples

Where a is an array:

{% assign x = a | map: i => i.foo.bar %}
{% assign x = a | where: i => i.foo == 'cheese' %}
{% assign x = a | where: item => item.bar != 'potatoes' %}
{% assign x = a | find: item => item.thing[0] > 42 %}
{% assign x = a | find_index: item => item.some[0] > 42 and item.thing < 5 %}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions