I repeat the or and and suggestion
That would fix this
Yeah I agree that's viable @Sam Mohr
I actually like that. I think even Zig does that right?
Here's the kind of example I'm worried about:
foo = |args|
abc!
|| xyz
That's what we had to add a test case for
If that || were indented by a single extra space, then it's all the sudden an operator
I think it should have to be a full INDENT
None of the rest of the parser requires that
I know
hm, or and and are interesting in this context :thinking:
it's a bit of strangeness budget but pretty minor
But when we move to token-based parsing, having just INDENT and DEDENT tokens to parse is nice
or maybe like...unusual-ness budget?
they're not exactly alien choices haha
There is a few well known languages with this
yeah
Python is the big one of course
Python, Zig, Elixir...
That's fighting for #1 language these days
I'm sure I can think of more
yeah that's what I mean by like "unusual rather than strange"
I think it would be more popular if not for C copying everywhere
most mainstream languages don't do it, but some do
the thing I dislike about it is that it means you can't use those words in DSLs because they have to become reserved keywords
but on the other hand, a thing I like about it is that except for historical reasons it's kind of silly to have && and || but not & and |
I think a total beginner to programming would understand or way faster than "it's like || which is two pipes because | meant bitwise originally"
yeah that's definitely true haha
the thing I dislike about it is that it means you can't use those words in DSLs because they have to become reserved keywords
What if they were operator overload-able? :P
That'd work for me
Wait
Short circuiting breaks
AND/OR is used in
CoffeeScript, Common Lisp, Emacs Lisp, Io, Logo, Lua, Modula-2, Perl, Perl6, PHP, Pliant, Python, Ruby, Scheme, Smalltalk
A few really popular languages in there
CoffeeScript has both if I remember right
Unless we can do something wacky where boolean short-circuits but nothing else does
I think maybe Ruby too?
Short circuiting breaks
Does it? I think it just means the signature of the function that implements it needs to be a bit funky.
Could you give a signature?
I don't think allowing it to be overloadable is a good idea
that sounds like a huge footgun
That sounds like Haskell
but I do think it's pretty rare to use them in DSLs
Are the griffons gonna come save us from mount syntax?
I'm sorry Smalltalk
I don't think allowing it to be overloadable is a good idea
Yeah, I kinda agree. For completeness tho, I was thinking along these lines:
and : (t, ()->t) -> t
seems worth a separate thread to gather feedback, but I'm generally open to the idea of and and or keywords replacing && and ||
also, we could continue to parse the current ones (maybe just ignoring the edge case) and have the formatter quietly turn them into and and || for you
and have the compiler warn if they're used as binops
Yeah I was thinking exactly the same thing
because a lot of beginners will trip over that out of habit
(coming from other languages)
{-# LANGUAGE BrokeYourShortCircuitingOperators #-}
Hey, nowhere did I suggest breaking short circuiting!
I know, sorry, I'm on a tear with the breaking short circuiting jokes, and it's past my bed time
Haha np
Also, I have to dig at Haskell every opportunity I get
Because I'm not smart enough to write a White Paper
same
Or read zero point style
I'm just a Monoid that's NOT in the category of endofunctors
I haven't fact-checked this, but here's what perplexity.ai says:
Also include PHP in the "and" and "or" category
it supports both?
oh this is interesting rationale: https://github.com/ziglang/zig/issues/272 (although we have ? for control flow, so we don't follow that convention anyway)
We have been having some parsing troubles in the interaction of our new |args| body function syntax and our || or binary operator syntax. Namely, || is both a list of zero arguments and also the "or" operator. To avoid having to require indentation when we see binary operators at the start of a line, why don't we instead switch to and and or for binary operators?
Collecting arguments from other threads.
Pros:
|| empty_arg_closuresCons:
Possible mitigations:
||, require it to not be on the start of a line, or be very clearly indented (to keep avoiding ambiguity)67 messages were moved here from #ideas > zero-arg functions & sugar by Sam Mohr.
Richard Feldman said:
oh this is interesting rationale: https://github.com/ziglang/zig/issues/272 (although we have
?for control flow, so we don't follow that convention anyway)
I was arguing for a Zig style keyword for this. I actually loved that style when I was on my Zig arc
The other contender here is not. Do we want to change !is_admin(user) to not is_admin(user)?
feels like that will create more parens
I'd say yes for consistency, as that one can be operator overloaded pretty easily
I'm not sure if it will
oh maybe not nm
:thinking: we could consider .not()
Yep
would we keep != at that point?
Python does that
So, not (hah!) that weird
it's kinda funny that right now you can have !foo!()
Actually, removing != means that you don't need to have == and != impls for custom types
just a single .equals() method
I don't think we want to remove !=
The problem is when we lose efficiency because of this, but that's pretty minor
either rename or keep as-is
I dunno, ! feels nicer to me than not
it's more concise, doesn't reserve a keyword, is easily understood
has symmetry with !=
I think we should keep that one as-is
A discussion on this from only 4 months ago: #ideas > `not` keyword instead of `!` prefix @ 💬
Same conclusion
It has symmetry of !condition and x != y
oh yeah I forgot about this point:
Richard Feldman said:
I don't have strong feelings about
notvs!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
good point, Past Me
hmmm
We'd have to pick a != equivalent we'd even consider, then
Or of course, get rid of !=
Ayaz Hafiz said:
I always find
notsuper 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 <>
a consistent theme in that thread is very strong support for != as the "not equals" operator
and if we have that, then we can't have "! only means effectful function"
at which point I'd agree with More Recent Past Me on this:
Richard Feldman said:
it's more concise, doesn't reserve a keyword, is easily understood
has symmetry with!=
although
I guess a counterargument is that != would never appear touching a letter (in practice)
so foo! would always stand out as a separate token from !=
plus they would be syntax-highlighted differently because one is part of a name and the other is an operator
so maybe it's fine if ! is only for effectful function names and !=
in terms of scannability
(although status quo feels fine there too, heh)
Is a not= b so terrible?
Actually I think I answered my own question
That does look terrible
I've been playing with different samples from various repos... and they all look fine to me. Here's an example.
is_hex : U8 -> Bool
is_hex = |b|
(b >= '0' and b <= '9')
or (b >= 'a' and b <= 'f')
or (b >= 'A' and b <= 'F')
I second Sam's argument that this is more beginner friendly
We could always do the same thing with !foo and _parse_ it but format it as not foo
I don't love the idea of using not though...
Maybe I've just been doing too much python, but... "that's what python does" is a reasonably strong argument in my book
That said, IIRC that did get a bit of getting used to when I first learned python
Particularly not vs != was a bit confusing
expect not is_hex('g')
expect not is_hex('\\')
expect not is_hex('-')
Imagine this in the context of effectful functions
expect !foo!()
vs
expect not foo!()
Honestly I think the not is a lot more readable in that situation
Too many !s going on
effectful functions that return booleans are pretty rare though
We would encourage Tags though for roc...
check_file! : Str => Result [Good, Bad] [IOError]
check_file! = |str|
if str == "good" then
Ok(Good)
else if str == "bad" then
Ok(Bad)
else
Err(IOError)
the only common one I can think of is !File.exists!(path) but actually I think that's kind of a counterexample, in that usually if you're doing something like "if it doesn't exist, then create it" you're leaving performance on the table compared to attempting to create it and then ignoring any "it already exists" errors
(2 syscalls vs 1)
expect is_hex('A') and not is_hex('g') or is_hex('0')
I think not is growing on me... but I want to keep!= and == is that rational?
it's what Python does
is_internet_connected!()
I think we should decouple the not discussion from the and and or discussion
and and or solve a problem with lambda syntax
not is stylistic only
If we switch to and and or, I'd argue we should at least _parse_ not and turn it into !
Those three often go together
that would make it effectively a reserved keyword
Yes
Well, mostly
haha
We can parse most constructs just fine, especially if we don't support WSA anymore
I don't think we should reserve a keyword and then not use it for anything semantically :big_smile:
In fact - in a non-WSA world, not doesn't have to be reserved at all in order to parse not foo
You could have a function called not() just fine, for example. You could also define that function without issue. Or have an arg with that name, etc.
I don't think we should reserve a keyword and then not use it for anything semantically
FWIW C++ does exactly this with and / or (and I think not?)
Most people use && / ||, but you can also do and / or and it means the same thing
Not that c++ is a shining example of a language
yeah fair point about the space having different meaning
at any rate, are there any concerns/objections about switching to and and or?
seems like the general sentiment so far has been neutral to positive
but that may just be because the alternative perspective hasn't been voiced in the thread yet :big_smile:
I think the main concern I heard was that you can't name things like that e.g. for a dsl
With some very slight caveats tho I think that can actually work out fine as a non-reserved keyword
For example:
and = |a, b| build_and_node(a, b)
and(1, 2)
In both of those cases, we just need a lookahead of 1 "token" (even tho the parser doesn't use those now!) to know that those can't possibly be operators
Actually no I take that back
That could be parsed as:
and = |a, b| build_and_node(a, b) and (1, 2)
That seems like it shouldn't parse that way, though I believe you that it would today
If the function body starts on the same line as the args, we shouldn't parse more statements
Unless they have at least 1 space of indent
Ahh true; that's not ambiguous in that particular case
a + b
and(1, 2)
... is ambiguous in that way tho
Or, more properly,
foo!()
and(1, 2)
We could keep the current rule where we're very particular about whitespace between the function and the parens
e.g. so if you actually wanted the and operator you'd have to write:
a + b
and (1, 2)
That feels fragile tho, particularly if that's the only place we need that constraint
I'd vote for making them reserved keywords
We can always relax that in the future
It works as a method, though
Only at the start of an expression
Otherwise it's almost-but-not-technically ambiguous
You could do (and(1, 2)) with zero ambiguity, or 1 + and(1, 2)
Oh, I meant with the dot in the front
Which is most of the usage for methods
Ah interesting
I was imagining a dsl for building up z3 expression
I'd expect left.and(right) in SD Roc
Ahh, true that works
I was imagining it as a function
It'd be weird if that was the one example of something you _had_ to call via SD
Which means we don't want it as a reserved keyword, unless methods get exempt from that keyword collision check
Err, I would still vote to make it reserved
Pretty sure that's how we'll check for SD types
My dsl can be andz(1, 2) instead and it's not terrible
What I _would_ love for such a DSL is operator overloading :P
Just some trivia
c++ allows both '&&' and '||', as well as 'and' and 'or'
Even c apparently has the <iso646.h> header which defines macros to turn the keywords into operators. It was used historically for users with keyboards that didn't have the symbols
0a6976e7-25e9-49af-b5d6-cf0a1ce7aae3.png
Here is a full list in the c header
I’ll just say this. People who put a space between the function and the arguments in PNC have questionable motives. I’m coming at you GNU
Anthony Bullard said:
I’ll just say this. People who put a space between the function and the arguments in PNC have questionable motives. I’m coming at you GNU
Yeah, I don't trust them too :big_smile:
I like the and and or keywords. They seem beginner friendly. Always perceived them friendly, as in kind and welcoming (syntax is so much about vibes).
I'm unsure aboutnot. It seems wrong to see two keywords after eachother like thing and not other_thing. Different syntax highlighting would help, but there's no such problem with !. There is just something clear about ! coming right before the operand, without a space. A solution to this particular problem would be thing and other_thing.not(), which brings the opearnd (in this case argument) together with the not operator. If that's the style that will be mainstream, that will look even more weird to outsiders than combining and, or and !. It also doesn't help that thing.not() reads backwards compared to how you would think about it in English.
Richard Feldman said:
it's kinda funny that right now you can have
!foo!()
Coming soon: ¿por_qué!?
I have a selfish two thumbs up (👍 and 👍) for replacing symbolic operators with reserved keywords words in general, and especially these two. (since today I'm mostly writing scripts that prioritize readability to a non-technical audience)
Bool.and and Bool.or mean that we can't make these operators reserved keywords, I think
I did something funky for Result.try where we parse the word "try" as its own AST node, and if it doesn't desugar to a LowLevelTry, then we convert it back to the word "try"
We could try do the same thing here?
Sam Mohr said:
Bool.andandBool.ormean that we can't make these operators reserved keywords, I think
we can just remove those
I think now that we do short-circuiting, it's probably a bit confusing to have them (for use in pipelines) because they don't short-circuit
Oh, that's fair
I was trying to do it in spite of what you said in case I could make it work somehow
But if they don't short circuit, that's a problem
yeah, the footgun aspect makes me think whatever convenience they offer isn't worth it
If we lose Bool.and, we lose the ability to use .and() in static dispatch. (Which is probably fine, but worth saying we're okay with it.)
(deleted)
If methods are allowed to have keyword names, then that restriction could go away
Which still is a restriction since it means you can't call the method like a normal function
But that seems minor to me
Oh I wonder if you could also refer to the function if it was qualified Bool.and. Like as long as it's got a dot before it you know it's the function not the keyword
I feel like that is something not worth worrying about yet. I think direct use of Bool.and is exceptionally rare and it is a pretty minimal cost to have to type |a,b| a and b
Probably less common in roc cause using Bool is so rare when tags are so easy and promoted in general.
Sky Rose said:
If we lose Bool.and, we lose the ability to use .and() in static dispatch. (Which is probably fine, but worth saying we're okay with it.)
I think this is good though - if it's a function (that takes two non-functions for arguments) then it's not short-circuiting and is error-prone
I think we're better off not having it
Bool.and in particular we shouldn't keep, yes. But would it be bad to have .and() available for DSLs?
Yes, we don't need to worry about this yet
yeah I think we can think about that later
it's obviously not blocking any DSLs since you can just add a letter as a workaround :big_smile:
for some other point, I know lean has some equivalent functions between functions that return Prop and functions that return bool. The latter usually get a b prefix, so eg eq for Prop and beq for bool. We could do the same with Bool.band and Bool.bor. Not exactly aesthetic.
Alternatively, And and Or are traits in rust, so overloading the trait overloads the meaning of a && b, and I've used that and BitAnd in some rust dsls to get nice and-y or-y syntax.
Last updated: Jun 16 2026 at 16:19 UTC