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

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

Open
zhaopengme opened this issue Aug 19, 2012 · 68 comments
Open

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

zhaopengme opened this issue Aug 19, 2012 · 68 comments

Comments

@zhaopengme
Copy link

"and" is and.

"or" is or.

but what is "not" ?

thanks!

@nickpearson
Copy link
Contributor

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.

@phoet
Copy link
Contributor

phoet commented Jun 9, 2013

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

@fw42
Copy link
Contributor

fw42 commented Jun 16, 2013

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 fw42 closed this as completed Jun 16, 2013
@retorquere
Copy link

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

@pathawks
Copy link
Contributor

if not p.url contains '/'

unless p.url contains '/'?

@retorquere
Copy link

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.

@Cam
Copy link

Cam commented Sep 9, 2014

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.

@courtyenn
Copy link

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.

@fw42
Copy link
Contributor

fw42 commented Feb 19, 2015

Reopened because of #526

@Blaisorblade
Copy link

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

ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
. I see why this is not on your roadmap — you'd need to replace that part of the parser.

@fw42
Copy link
Contributor

fw42 commented Feb 19, 2015

@trishume and @pushrax might have thoughts

@pushrax
Copy link
Contributor

pushrax commented Feb 20, 2015

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

@trishume
Copy link
Contributor

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

@pushrax
Copy link
Contributor

pushrax commented Feb 22, 2015

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.

@Blaisorblade
Copy link

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.

@mikebridge
Copy link

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

@fw42
Copy link
Contributor

fw42 commented Mar 30, 2015

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

@mikebridge
Copy link

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

@Tamriel
Copy link

Tamriel commented Jan 16, 2016

It's a shame this is still an issue.

@fw42
Copy link
Contributor

fw42 commented Jan 16, 2016

@Tamriel: Patches welcome

@pushrax
Copy link
Contributor

pushrax commented Jan 16, 2016

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.

@rickydazla
Copy link

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?

@mikeyhew
Copy link

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

@nickpearson
Copy link
Contributor

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.

@Blaisorblade
Copy link

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?

@jmarca
Copy link

jmarca commented Oct 11, 2018 via email

@aklef
Copy link

aklef commented Aug 10, 2019

Bump because I ran into this issue this afternoon and had to waste time understanding why "not" didn't exist and coming up with a workaround..

A "not" operator would be really nice

@haselwarter
Copy link

lol

@fer-rum
Copy link

fer-rum commented Jul 13, 2020

Somehow, a template language that has been unable (or unwilling) to implement not in nearly 8 years is the foundation of several businesses and nearly all pages hosted on github and gitlab. 😲

@tobiasvl
Copy link

I also ran into this issue just now (not with Shopify, but GitHub Pages) and spent some time figuring out that not isn't supported. GitHub unhelpfully just told me that the build failed, not why.

It's really quite astonishing that there's no not operator, and it doesn't seem to be mentioned in the Liquid docs either. Its absence is a major gotcha for anyone remotely familiar with programming, so a section about this issue in the section about truthiness/falsiness or something would probably be nice. If you're not planning on adding support for it, that is.

@fer-rum
Copy link

fer-rum commented Jul 23, 2020

In case somebody stumbles over this in the future:

Since unless is basically the way of expressing if not and nested loops are an implicit and you can express
if a and not b as

{% if a %}
    {% unless b %}
        <p> a and not b </p>
    {% endunless %}
{% endif %}

Sadly there seems to be no good way to express an or so you will have to apply DeMorgan's laws. Figuring out the resulting liquid expression is left as an exercise to the reader 😈

For more complex expressions I would recommend to use intermediate variables and evaluate the sub-expressions step by step. This is pretty tedious however and hard to maintain.

nickcharlton added a commit to nickcharlton/site that referenced this issue Jun 20, 2021
This commit introduces a new Jekyll filter, which allows us to take the
global `site.posts` collection and include or exclude certain tags.

This is needed as the included `where_exp` filter doesn't allow you to
negate collection contents and isn't being implemented.

Shopify/liquid#138
nickcharlton added a commit to nickcharlton/site that referenced this issue Jun 20, 2021
This commit introduces a new Jekyll filter, which allows us to take the
global `site.posts` collection and include or exclude certain tags.

This is needed as the included `where_exp` filter doesn't allow you to
negate collection contents and isn't being implemented.

Shopify/liquid#138
nickcharlton added a commit to nickcharlton/site that referenced this issue Jun 20, 2021
This commit introduces a new Jekyll filter, which allows us to take the
global `site.posts` collection and include or exclude certain tags.

This is needed as the included `where_exp` filter doesn't allow you to
negate collection contents and isn't being implemented.

Shopify/liquid#138
@notrealdev
Copy link

Hi, in JS i have condition

if ( A || ( B && C ) ) {
    // Code here
}

// How I can do with Liquid?

{% if A or B and C  %}
    Hello
{% endif %}

@mahnunchik
Copy link

Any news?

@pigsflew
Copy link

pigsflew commented Dec 21, 2021

I don't know if this is really the same issue, but i'd love a not-where clause for filtering lists. IE: {% assign promos = site.pages | where: 'homepage', true | except: 'show', true %}.

That way anything that isn't true will trigger the thing, even if it's not actually a 'falsey' value.

If this is a different issue please let me know; i didn't find this exact thing already reported and wasn't sure if i should create something new.

@rianfloo
Copy link

Any news 9 years later? :(

@moseleyi
Copy link

Soon to be 10.. still no solution for this?

@fer-rum
Copy link

fer-rum commented Apr 20, 2022

Maybe they want to keep it around as a running gag. 🤡

@TenguTech
Copy link

Yes, more logic in Liquid would be nice.

On the time period to resolve this issue, there is an Atlassian Cloud Jira card in that will be 11 years old in 4 days :)

@erikplatte
Copy link

To use a NOT statement in Liquid this worked for me:
Simply put the NOT true in an else statement

{% if a=12 %}
[put if true result here]
{% else %}
[put if NOT true result here]
{% endif %}

@TWiStErRob
Copy link

@erikplatte {% unless a=12 %} exists for that reason... the problem in this issues is more complex boolean expressions, e.g. a and not b.

@UlyssesZh
Copy link

+1 for the feature! I need to express the following logic:

{%- if !layout.no_gitalk and !page.no_gitalk and (jekyll.environment == 'production' or site.debugging_gitalk) -%}

Currently I will need three nested if/unless to express this logic...

@tim-harding
Copy link

Is this on the table 11 years later? Would love a simpler way to negate an assignment than this:

assign negated = true
if myvar
  assign negated = false
endif

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

No branches or pull requests