liquid
liquid copied to clipboard
How to express logic "and","or", "not"?
"and" is and.
"or" is or.
but what is "not" ?
thanks!
To work around this, I've always used nested if
/unless
statements, which isn't pretty but it works.
So, if you have this in a language that has a not
operator:
if a && b && !c
# ...
end
then you can do this in Liquid:
{% if a and b %}
{% unless c %}
...
{% endunless %}
{% endif %}
It can get tricky if you need complex else
logic, but most times this works for me.
@dapengme what is your specific usecase? you can also use var != 'value'
Since this is not really an issue but more of a question, and it's pretty old, I will close this for now. Please re-open if necessary.
Without "not", it is quite awkward to express something like "if not p.url contains '/'"
if not p.url contains '/'
unless p.url contains '/'
?
That works only for simple, not combined conditions. I've worked around it, but "not" is a legitimate boolop that is not entirely replacable by unless.
Sometimes it would make a lot more sense to have things that behave like "does not contain", rather than an "unless" statement. It would keep things cleaner and offer more options.
I agree with Cam and AllThatIsTheCase. It is difficult to check if, for example, a collection of product.tags and customer.tags do not contain the tag "wholesale" without a "not contains" operator because of the combined condition.
Reopened because of https://github.com/Shopify/liquid/issues/526
From #526:
same way having support for parentheses would be nice to have
Ah. Sounds like the problem is the parser support for not, because you're not using a parser generator or something like that — and you can't parse the context free grammar of expressions with parentheses with regular expressions such as https://github.com/Shopify/liquid/blob/12d526a05c0654dc67e350f170f91d62fd4875b7/lib/liquid/tags/if.rb#L14. I see why this is not on your roadmap — you'd need to replace that part of the parser.
@trishume and @pushrax might have thoughts
We could totally implement parentheses with the new parsers, but it would probably break some templates that are using them regardless of whether they do anything or not. We even have a test asserting that parentheses are broken :(
If this feature is wanted, though, I would be happy to do the necessary work, which would include killing certain portions of the lax parser (which we want to do eventually anyway, right?)
The other option is to have some kind of super-conditional tag or syntax that is only available with liquid-c or the strict ruby parser. That way it could be opt-in and not break existing templates.
Maybe call it cond
, if*
or if+
. Though the fact that fancy expression features work in that tag but not if would be weird.
Or it could be an opt in for expressions. Maybe some special kind of augmented parentheses in which real expression syntax is valid. Maybe something like the magic test command in bash: {% if [this && (that || other)] %}
That would work, but it would also be really confusing going forward. I think if we do this at all we should work towards unifying syntax and formally specifying it.
As a programmer-minded user, I'd be happy to set a flag to enable an extended syntax, where my parentheses mean something, and promise I don't use weird templates.
One could also argue that flag should be orthogonal to strict vs lax mode. In fact, when transitioning, I'll probably need to switch to strict mode to check our templates actually parse (i.e., probably, they should contain no parentheses), before enabling a new syntax where these parenthesis suddenly can be used and have meaning.
+1 for the formal specification. Right now there's some divergence among various flavours, e.g. the "keyword" style of Jekyll's parameters in include
vs. the ruby-variable style of Shopify Liquid.
I'm afraid neither Shopify nor Jekyll can make any drastic changes now since a huge number of users already depends on what we/they have :-)
@fw42 Yeah, I'm thinking more of myself here Re: specs---I'm porting liquid to another environment and was hoping not to accidentally end up in the same position. :)
It's a shame this is still an issue.
@Tamriel: Patches welcome
In this case it's not simple at all and any patch we accept is going to take a long time and a lot of testing/validation on our end.
You're really concerned that it would "break some weird-ass templates that are using them regardless of whether they do anything or not"? I mean, correct me if I'm wrong but parentheses currently don't do anything, right?
@pushrax why not just change the behaviour of boolean expressions to the normal way, and put that change in the next major version? And we could allow people to opt in in the current version by using a declaration at the top of the file or something.
I've used the boolean-expressions
gem to handle boolean logic like requested, though not in the context of Liquid. I'll just leave this link here in case it helps someone who wants to submit a patch that either uses this gem (last updated in 2012) or extracts its functionality. https://github.com/meh/boolean-expression
It's not immediately clear how to use it from the README, so see the #evaluate
tests for more examples. The basic gist is Boolean::Expression[<expression-string>][<symbols that are true>]
, like Boolean::Expression['a && b'][:a, :b]
(which returns true, because both :a
and :b
are passed as true
values).
I agree that this is a big change to Liquid that will require tons of testing, but it would be nice to be able to write more complex if
statements, so maybe this will help someone.
And we could allow people to opt in in the current version by using a declaration at the top of the file or something.
Given what the compatibility requirements seem, maybe it'd be more realistic to require people to opt-in to the new syntax. I started sketching details, then realized I already did to some extent.
The issue is recomposing conflicting requirements. @pushrax had volunteered to do the work, but added:
[Opt-in new syntax] would work, but it would also be really confusing going forward. I think if we do this at all we should work towards unifying syntax and formally specifying it.
while @fw42 replied:
I'm afraid neither Shopify nor Jekyll can make any drastic changes now since a huge number of users already depends on what we/they have :-)
Options include:
- go for an opt-in better syntax: ugly but better than now. Add an API call to enable that globally for who wants it. Possibly, reuse Python's
future
solution, so that you declare in advance some future major version will enable this by default, in case you want the API to get eventually sane. Or wait for the whole community to test the new syntax before deciding what to do. - close the issue as won't fix.
- change syntax and check for breakage (so complicated that it didn't happen in one year)
- change syntax and ignore breakage (no)
@pushrax, @fw42, is option (1) doable on your end? The only disadvantage seems "ugliness" — I care about software's elegance, because it makes a practical difference, but it seriously seems less ugly than (2). Are there further obstacles? Thoughts?
I think this issue needs to be broken in to 2 parts.
- Some of the issues are about not having a NOT operator
- Adding support for parentheses to handle order of operation
I think with any problem its better to start with the easy wins so I have just committed code around the first issue to hopefully get it out of the way so that only the second issue remains which from discussion is the one that is going to cause the biggest impact on backwards compatibility. Also It requires a lot more complexity by needing to build an AST with nodes and then resolving them back in order.
Comments on the Pull Request are appreciated.
This simplifies the following
{% if a and b %}
{% unless c %}
...
{% endunless %}
{% endif %}
to be a single line
{% if a and b and not c %}
....
{% endif %}
It does not support the following as it is already available with unless
{% if not a and b and not c %}
....
{% endif %}
Already existing unless
{% unless a and b and not c %}
....
{% endif %}
To be clear: your patch does not support not
at the beginning of the expression, right?
Are your two last examples supposed to be equivalent? If so I don't get the precedence rules or something. I infer from your patch that not
binds most tightly.
Pretending we have parentheses, unless a and b and not c
should mean unless (a and b and (not c))
, that is if not (a and b and (not c))
, that is if (not a) or (not b) or c
, with or
instead of and
.
I get the first code example, but the second is weird. Are you saying there can be only one not
and only as the last one? Also in common Boolean logic:
!a and b and !c <> !(a and b and !c) === !a or !b or c
I'm using parentheses above because as far as I know unless
negates the condition altogether.
@TWiStErRob the patch supports if a or not b or not c
, because it parses and not
and or not
, but doesn't support not
on its own.
On the example I agree, we commented simultaneously 😄