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 ...
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 +
Which I think is more consistent, as well as more readable
What do people think?
if we do that, is our "not equals" binary operator still != or does it become something else?
that was the original reason I went with the ! unary prefix operator - wanting symmetry with !=
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
Python still has != even with not, I think it's fine if we have it alongside not.
My main dislike is this need for parentheses around List.isEmpty items
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 :
But yeah, this is more aesthetic, so a minor thing. Just figured I'd get the discussion out in the open
I like not
I'm open to the idea of not but I think it would be strange to have not and still have !=
some things other languages use besides !=
ne<>/==/=\=~==/= is visually my favorite, though you're right that there are not yet any operators over 2 chars
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"
I'd vote /= for the equals sign lowering the weirdness, but I agree that <> has visual meaning and is nicely symmetrical
I also like how =/= looks although I dislike that it would be the only 3-char binary operator in the language
So, I'd be okay with <> if it meant we got not
Do you think new users would get <>?
I think so, although it's pretty old-school; languages that use it include BASIC, VB, SQL, COBOL, Pascal, OCaml, and F#
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.)
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.
yeah I agree, but it also looks like the += *= -= /= family of operators in other languages
Ohhhhh
Yeah, bad idea
=/ doesn't have that problem but it does look like the old-school version of the :confused: emoji
lol, let's not.
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
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.
yeah ~= to me looks like "approximately equal to"
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.
Python also uses and and or instead of && and ||. What are your thoughts about that?
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
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:
! over not, but either is ok!= over any other alternativesand and orI always find not super annoying in Python
because != and = exist but not ! and sometimes i forget because in other languages it's available
keeping != seems like a good idea too. i mean even sql added != because it was more familiar than <>
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 !=.
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?
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.
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.
Is utf8 out of the question? ≠
It reads well, but probably is a pain to write (I personally copy-paste it)
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.
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:
tlaplus uses # for "not equal" as it's I guess graphically similar to =/=
edit: unnecessary quote
does roc really need comments? /s
I'd favor any of:
!= paired with !, &&, ||. C style, no weirdness budget expended, avoids the space-separated-args awkwardness/ambiguity that Brendan mentioned.!= 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.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)
!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
At the moment you can do function arg1 arg2 |> Bool.not
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.
Same for Bool.true and Bool.false
Which is why it may not be that bad a thing for other boolean-based operators to be awkward
I actually think we should make true and false be keywords, but that's a separate topic :big_smile:
overall honestly this one feels like we should stay with status quo for now
Totally fine with me
Richard Feldman said:
I actually think we should make
trueandfalsebe 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.
I agree with that viewpoint
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??
I don't think there is a meaningful difference between calling them Boolean literals and keywords
Also, I think making them extra typing in roc is good for two reasons:
Bool.true and Bool.false are almost never used directly.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
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).
@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.
Fair point. In favor of adding not, then, as an alias of !. Everything else the same.
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