liquid icon indicating copy to clipboard operation
liquid copied to clipboard

How to express logic "and","or", "not"?

Open zhaopengme opened this issue 11 years ago • 66 comments

"and" is and.

"or" is or.

but what is "not" ?

thanks!

zhaopengme avatar Aug 19 '12 05:08 zhaopengme

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.

nickpearson avatar Sep 13 '12 14:09 nickpearson

@dapengme what is your specific usecase? you can also use var != 'value'

phoet avatar Jun 09 '13 13:06 phoet

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.

fw42 avatar Jun 16 '13 12:06 fw42

Without "not", it is quite awkward to express something like "if not p.url contains '/'"

retorquere avatar Aug 17 '14 00:08 retorquere

if not p.url contains '/'

unless p.url contains '/'?

pathawks avatar Aug 17 '14 00:08 pathawks

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.

retorquere avatar Aug 17 '14 00:08 retorquere

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.

Cam avatar Sep 09 '14 04:09 Cam

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.

courtyenn avatar Feb 16 '15 06:02 courtyenn

Reopened because of https://github.com/Shopify/liquid/issues/526

fw42 avatar Feb 19 '15 19:02 fw42

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.

Blaisorblade avatar Feb 19 '15 19:02 Blaisorblade

@trishume and @pushrax might have thoughts

fw42 avatar Feb 19 '15 20:02 fw42

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?)

pushrax avatar Feb 20 '15 02:02 pushrax

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)] %}

trishume avatar Feb 22 '15 01:02 trishume

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.

pushrax avatar Feb 22 '15 01:02 pushrax

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.

Blaisorblade avatar Feb 22 '15 07:02 Blaisorblade

+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.

mikebridge avatar Mar 30 '15 22:03 mikebridge

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 avatar Mar 30 '15 22:03 fw42

@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. :)

mikebridge avatar Mar 31 '15 01:03 mikebridge

It's a shame this is still an issue.

Tamriel avatar Jan 16 '16 00:01 Tamriel

@Tamriel: Patches welcome

fw42 avatar Jan 16 '16 02:01 fw42

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.

pushrax avatar Jan 16 '16 06:01 pushrax

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?

rickydazla avatar Jan 17 '16 05:01 rickydazla

@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.

mikeyhew avatar Feb 16 '16 17:02 mikeyhew

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.

nickpearson avatar Feb 16 '16 18:02 nickpearson

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:

  1. 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.
  2. close the issue as won't fix.
  3. change syntax and check for breakage (so complicated that it didn't happen in one year)
  4. 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?

Blaisorblade avatar Feb 16 '16 18:02 Blaisorblade

I think this issue needs to be broken in to 2 parts.

  1. Some of the issues are about not having a NOT operator
  2. 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.

evulse avatar Aug 17 '16 16:08 evulse

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 %}

evulse avatar Aug 17 '16 16:08 evulse

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.

Blaisorblade avatar Aug 17 '16 16:08 Blaisorblade

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 avatar Aug 17 '16 16:08 TWiStErRob

@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 😄

Blaisorblade avatar Aug 17 '16 17:08 Blaisorblade