Stream: ideas

Topic: `not` keyword instead of `!` prefix


view this post on Zulip Sam Mohr (Sep 11 2024 at 03:53):

Someone earlier suggested that we should consider adding a not keyword to Roc, since we're already so Pythonic for our keyword preference. I think that it would look much better than our current options:

# current
if !(List.isEmpty items) then ...

# pipelined
if List.isEmpty items |> Bool.not then ...

# new syntax
if not List.isEmpty items then ...

view this post on Zulip Sam Mohr (Sep 11 2024 at 03:57):

If we move to something like try instead of ? and ! as part of a function's name, then with this we'd basically have no unary operators, just math-based binary ones like == and +

view this post on Zulip Sam Mohr (Sep 11 2024 at 03:57):

Which I think is more consistent, as well as more readable

view this post on Zulip Sam Mohr (Sep 11 2024 at 03:57):

What do people think?

view this post on Zulip Richard Feldman (Sep 11 2024 at 03:58):

if we do that, is our "not equals" binary operator still != or does it become something else?

view this post on Zulip Richard Feldman (Sep 11 2024 at 03:59):

that was the original reason I went with the ! unary prefix operator - wanting symmetry with !=

view this post on Zulip Richard Feldman (Sep 11 2024 at 03:59):

Elm has not and uses /= for its "not equal" operator - you can think of it as short for =/= - but I thought it wasn't worth the weirdness budget cost

view this post on Zulip Sam Mohr (Sep 11 2024 at 03:59):

Python still has != even with not, I think it's fine if we have it alongside not.

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:00):

My main dislike is this need for parentheses around List.isEmpty items

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:01):

This language, like Elm, provides lots of tools for avoiding parentheticals. I personally find the absence of not weird in face of other keywords being used, like then and is after conditionals instead of :

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:02):

But yeah, this is more aesthetic, so a minor thing. Just figured I'd get the discussion out in the open

view this post on Zulip Luke Boswell (Sep 11 2024 at 04:20):

I like not

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:33):

I'm open to the idea of not but I think it would be strange to have not and still have !=

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:34):

some things other languages use besides !=

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:35):

=/= is visually my favorite, though you're right that there are not yet any operators over 2 chars

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:35):

I don't feel strongly attached to it, but I do remember learning about <> in BASIC like 30 years ago and being like "oh sure, either it's less than or greater than, in other words anything but equal"

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:36):

I'd vote /= for the equals sign lowering the weirdness, but I agree that <> has visual meaning and is nicely symmetrical

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:37):

I also like how =/= looks although I dislike that it would be the only 3-char binary operator in the language

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:37):

So, I'd be okay with <> if it meant we got not

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:37):

Do you think new users would get <>?

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:39):

I think so, although it's pretty old-school; languages that use it include BASIC, VB, SQL, COBOL, Pascal, OCaml, and F#

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:39):

That's my concern, though it's the Ocaml operator too, there's plenty of precedent if you look past the current most popular languages (e.g. Python, JS, etc.)

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:41):

The nice thing about /= in my eyes is that it works the same way as the \foo lambda operator. If you "squint", it looks like the underlying symbol.

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:41):

yeah I agree, but it also looks like the += *= -= /= family of operators in other languages

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:41):

Ohhhhh

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:41):

Yeah, bad idea

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:42):

=/ doesn't have that problem but it does look like the old-school version of the :confused: emoji

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:42):

lol, let's not.

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:43):

I don't have strong feelings about not vs ! but I do appreciate that if we're making ! mean "an effect is happening here" that it makes it even easier to scan a chunk of code for effects if that is the only thing that ! means

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:43):

Not a fan of ne if we use == for equals. So the only candidate besides <> is ~=, which I think is less weird than <>, but still weird.

view this post on Zulip Richard Feldman (Sep 11 2024 at 04:44):

yeah ~= to me looks like "approximately equal to"

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:45):

We could also get rid of != and expect users to write not x == y, but that moves part of the operation outside of the space between the operands.

view this post on Zulip Oskar Hahn (Sep 11 2024 at 04:47):

Python also uses and and or instead of && and ||. What are your thoughts about that?

view this post on Zulip Sam Mohr (Sep 11 2024 at 04:52):

I'm in favor. I just checked and Roc doesn't current have bitwise and or or, so there's no asymmetry to worry about

view this post on Zulip Brendan Hansknecht (Sep 11 2024 at 05:13):

I am not a fan of not due to precedence in a language that uses spaces for function args. not (fn a b c). Even if the parents aren't required, they feel necessary to group correctly

But I also don't think it is a big deal at all.

I think I would still significantly prefer != to anything. It is the defacto essentially everywhere.

Though I often forget about and and or and instead default to && and ||. I think that and and or are strictly the better solution here.

So I guess I would vote for:

  1. ! over not, but either is ok
  2. != over any other alternatives
  3. Switching to and and or

view this post on Zulip Ayaz Hafiz (Sep 11 2024 at 05:43):

I always find not super annoying in Python

view this post on Zulip Ayaz Hafiz (Sep 11 2024 at 05:44):

because != and = exist but not ! and sometimes i forget because in other languages it's available

view this post on Zulip Ayaz Hafiz (Sep 11 2024 at 05:45):

keeping != seems like a good idea too. i mean even sql added != because it was more familiar than <>

view this post on Zulip Oskar Hahn (Sep 11 2024 at 07:13):

I like Roc. You did so many good decisions, that I have more confidence in your experience, then in my opinion.

My opinion is not over !.

I think in the example not (fn a b c) the parents are optically required, however you choose not o !. When you write!fn a b c, the ! is not not visible enough for me. And it looks even weirder, when fn returns a Task: !fn! a b c.

I think I like keywords over words, because our keyboards are made for writing words. For a non English (or !English :stuck_out_tongue_wink: ) keyboard, you don't know if it is hard to type a symbol. But for any latin keyboard, it will always be easy to type words.

I still think, that you should keep !=, if you also keep ==. I think other symbols like <> are strange, if you compare other types then numbers (hasAttrA <> hasAttrB). Python also has not and !=.

view this post on Zulip Kilian Vounckx (Sep 11 2024 at 08:03):

I don't really care about not vs !, but I think we should think about the parens a bit more. For example, how would this parse: foo not bar x? Is it foo (not bar x), or a parse error because it tries to use not as a function argument?

view this post on Zulip Isaac Van Doren (Sep 11 2024 at 12:23):

I would prefer to leave things they way they are here. I definitely like having != and I think it would be a mistake to remove or change that as it is so familiar. I am very rarely annoyed by having to use parens when using a leading !, and introducing a special ability to omit parens for not seems confusing.

view this post on Zulip Matthieu Pizenberg (Sep 11 2024 at 12:34):

My issue with <> is that equality does not require order. And this symbol kind of implies order. I don’t mind /= or =/= or != and I like not personally.

view this post on Zulip Matthieu Pizenberg (Sep 11 2024 at 12:40):

Is utf8 out of the question? ≠
It reads well, but probably is a pain to write (I personally copy-paste it)

view this post on Zulip Sam Mohr (Sep 11 2024 at 14:57):

Matthieu Pizenberg said:

Is utf8 out of the question? ≠
It reads well, but probably is a pain to write (I personally copy-paste it)

I think the only way this is viable is that we auto-format != or =/= to the unicode version. But I think it's a dangerous barrier to cross.

view this post on Zulip Richard Feldman (Sep 11 2024 at 16:27):

Matthieu Pizenberg said:

Is utf8 out of the question?

yeah I think we should only ever use ASCII for language keywords and operators :big_smile:

view this post on Zulip shua (Sep 11 2024 at 17:33):

tlaplus uses # for "not equal" as it's I guess graphically similar to =/=

edit: unnecessary quote

view this post on Zulip shua (Sep 11 2024 at 18:08):

does roc really need comments? /s

view this post on Zulip Kevin Gillette (Sep 11 2024 at 18:13):

I'd favor any of:

  1. != paired with !, &&, ||. C style, no weirdness budget expended, avoids the space-separated-args awkwardness/ambiguity that Brendan mentioned.
  2. != paired with not, and, or, while doing something to visually disambiguate these written operators from calls (again per Brendan's point). The weirdness factor here (from mainstream prospective) wouldn't be from the keyword operators (Python has all these), but rather from the spaced arguments.

view this post on Zulip Brendan Hansknecht (Sep 11 2024 at 18:16):

One extra note here, I think it is easy for me to forget that custom syntax highlighting is a thing when chatting a zulip.

As long as not has a different color, it should parse just fine without parens for the most part. Though not a == b will probably always be confusing as to whether it is (not a) == b or not (a == b)

view this post on Zulip Kevin Gillette (Sep 11 2024 at 18:16):

!function arg1 arg2 has always looked strange to me, since it looks like the function itself is negated/modified, rather than the result of the call, thus the status quo doesn't feel great in this regard.

function arg1 arg2 |> not would look better to me

view this post on Zulip Kilian Vounckx (Sep 11 2024 at 18:18):

At the moment you can do function arg1 arg2 |> Bool.not

view this post on Zulip Sam Mohr (Sep 11 2024 at 18:20):

Based on why other languages do awkward things with booleans (see Gleam's lack of an if statement), I think Bool.not is intentionally awkward to push devs to use tag unions over booleans wherever possible.

view this post on Zulip Sam Mohr (Sep 11 2024 at 18:20):

Same for Bool.true and Bool.false

view this post on Zulip Sam Mohr (Sep 11 2024 at 18:21):

Which is why it may not be that bad a thing for other boolean-based operators to be awkward

view this post on Zulip Richard Feldman (Sep 11 2024 at 18:24):

I actually think we should make true and false be keywords, but that's a separate topic :big_smile:

view this post on Zulip Richard Feldman (Sep 11 2024 at 18:25):

overall honestly this one feels like we should stay with status quo for now

view this post on Zulip Sam Mohr (Sep 11 2024 at 18:25):

Totally fine with me

view this post on Zulip Ricardo Valero de la Rosa (Sep 11 2024 at 22:57):

Richard Feldman said:

I actually think we should make true and false be keywords, but that's a separate topic :big_smile:

I know I might be in the minority here, and I’m just being very nitpicky, but I'm kind of against this.

I love Roc’s minimalistic design with few keywords, which keeps the language clean and simple. If Bool.true or Bool.false are used often in a file, importing them with import Bool exposing [true, false] achieves the same effect without adding unnecessary keywords.

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 00:52):

I agree with that viewpoint

view this post on Zulip Peter Marreck (Sep 12 2024 at 12:13):

I like having both ! and not.
In Elixir there is this distinguishing of the two types of operators where &&, || and ! would accept "truthy" and "falsey" values ("falsey" being only false or nil, and "truthy" being everything else), while and, or and not would ONLY accept booleans.
Obviously in a typed compiled language this would automatically be more restrictive (which is good) but I just found this to be a useful distinction which is why I am mentioning it for those unfamiliar with Elixir.
I also like != (which via ligatures becomes the crossed-out equals sign in my editors).
I haven't developed an opinion on Bool.true vs. true yet, but I never found anything wrong with treating the 2 Boolean values as fundamental literals in a language (I never thought of them as "keywords", I thought of them the same way you'd think of 0 and 1 if you were dealing only with a Bit type that accepted only 2 possible values), and initially, Bool.true just seems like more unnecessary typing to me... We don't call other literals "keywords", so when did people start to think of true and false not as Boolean literals but as keywords??

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 13:49):

I don't think there is a meaningful difference between calling them Boolean literals and keywords

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 13:53):

Also, I think making them extra typing in roc is good for two reasons:

  1. Bool.true and Bool.false are almost never used directly.
  2. It pushes people to properly named tags instead of bools. Which will always be better style in roc instead of using bools.

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 13:54):

Also, if someone wants true and false directly in a file, they can just import them directly or define them directly. So no need for roc to make that the defaults:

import Bool exposing [true, false]

Or

true = Bool.true
false = Bool.false

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 13:55):

Given we never see those in practice. I think it is pretty clear that users don't really care about using Bool.true and Bool.false even if they are a bit quirky (which I still think is a good thing to push people to use tags instead).

view this post on Zulip Brendan Hansknecht (Sep 12 2024 at 13:58):

@Peter Marreck for ! and not what are you actually recommending with your comment? Roc doesn't have the concept of "truthy" and "falsey" in the language at all.

view this post on Zulip Peter Marreck (Sep 12 2024 at 20:43):

Fair point. In favor of adding not, then, as an alias of !. Everything else the same.

view this post on Zulip Aurélien Geron (Sep 12 2024 at 23:28):

As a Python programmer, I love not, and, or, and even in. Python code often feels like you're reading plain English, it's very pleasant, and it ties in well with Roc's Friendly goal.

Also, Python is the most popular language today, and it would be more welcoming to that huge community if we kept the operators similar. It's not like any of the alternatives are clearly superior anyway, so rather than mix styles, I'd vote for having the same operators as Python.

Of course people coming from C/C++/Java/... could argue that we should choose &&, || and ! instead, but that's just Stockholm syndrome. :wink: Joke aside, I could also get behind making logic operators similar to C++ or Rust (although I prefer Python's style), just as long as we don't mix styles.


Last updated: Jun 16 2026 at 16:19 UTC