Ayaz Hafiz said:
how sure are you all about that syntax?
is it (in pre 0.1 terms .. ) final?
I showed it to some people too, has/ :=/@ seem to be a source of confusion
I think at least the wording is already planning to change due to has being unclear especially when used in multiple contexts.
As an extra note, I would assume that := and @ will almost always seem confusing to people not familiar with roc. They are not specifically related to abilities.
They are for opaque types which is not really in other languages. So kinda something you just have to learn that is unique to roc.
Though of course the syntax could theoretically change.
yea I know and can empathize the rationale.
but perhaps we could come up with something cleaner
that as a bonus would also not make some folks say "ew walrus" (I can see the hackernews comments coming a light year away)
optimizing for hackernews comments seems pretty low priority to me, but yeah, feel free to recommend other syntax or open discussion in #ideas
Always nice to improve syntax where possible and help make the language easier for beginners.
lol I was kinda kidding but yea well the underlying thing to notice is when the toxicity of said comments might reflect an actual painpoint (disguised by orange) for someone that just sees the lang for the first time
but yea I think we agree here
+1 on changing the abilities syntax and trying to optimize for developer experience. I would also caution against trying to optimize for HN or other forums. the vocal majority is not representative, and forums are often not conducive to understanding the whole context. Of course, in isolation features should feel pleasant regardless
I definitely think the abilities syntax should change
I'm open to changing the syntax for opaque types, but I don't think there is an obvious better alternative
on the other hand, with abilities we've discussed multiple alternatives we think are better, and the tricky part is picking a specific design :laughing:
An opaque keyword could work:
opaque Username = Str
Instead of:
Username := Str
Sidenote: I've also come to realize that "the vocal majority is not representative" is a valuable life lesson :)
FWIW, I would love to move more in the direction of having keywords for both := and : (when used as an alias, not an annotation).
(which is to say, I like the suggestion above of opaque Username = Str)
Keywords are much more discoverable / searchable / etc
a downside of that is that now identifiers like opaque and alias become reserved keywords and can no longer be used in userspace
and yeah they're less discoverable/searchable but I think that's less of a downside for super common language primitives; people might not know what they mean when glancing at Roc code and not knowing Roc, but anyone who reads the tutorial will know what they mean right away
(contrast with ability for example, which is something I suspect most Roc users will use rarely to never - so discoverability/searchability is a bigger upside)
: is a bit more confusing since it's overloaded between type annotations and type aliases.
... but I digress
Richard Feldman said:
a downside of that is that now identifiers like
opaqueandaliasbecome reserved keywords and can no longer be used in userspace
perhaps they can be soft-keywords like in Kotlin?
interesting! @Joshua Warner do you think a soft-keywords design would work okay in the context of a lexer?
Some keywords will need to be 'hard' - specifically, I think 'if' / 'else' / 'then' / 'when' / 'is' all need to be "hard" in order for the "blocks" idea to work out
(this is because we need to handle those specially, and allow indented blocks to come afterwards)
Anything that doesn't allow an indented block immediately afterward can be a soft keyword.
hmmmm interesting!
opaque / alias would presumably come at the beginning of the line - so it'd be "annoying" if someone tried to name a function or local variable after those. You'd have to 'escape' such uses by e.g. wrapping them in parens.
couldn't we disambiguate during parsing?
e.g. opaque Foo and alias Foo is clearly a keyword usage because of capitalization, since opaque Foo = wouldn't be allowed
also a b c = is a parse error, so all we need to do is read the next token after opaque or alias to see what it is (or if it's invalid), give or take comments I suppose
I think that's possible.
so it's either opaque = or opaque : (assuming we've changed to alias for type aliases) for identifiers, and opaque Foo for types
and same for alias
38 messages were moved here from #beginners > Ad Hoc Polymorphism by Richard Feldman.
I haven't used kotlin or soft-keywords in general, but I think that just looks more confusing. Totally could be first look bias/lack of experience, but I like the idea of only having clear hard keywords.
if opaque and alias are common keywords, I think it would be very strange to see them as variable names. I guess it is less jarring of an idea for uncommon keywords like ability.
yeah opaque seems like it could come up in graphics systems, and alias is a common term for a person's alternate name
very true
Aligning those names better with what other languages use might help. e.g. something like private and type, respectively.
Then at least fewer programmers would expect to be able to use those as identifiers
... but the soft keywords approach is definitely workable, both in the current parser and the [possible/planned future] lexer[-based parser] - and seems much preferable to me to just using the :=/: symbols.
I definitely currently prefer the symbols over soft keywords but :shrug:. I'm sure I would get used to either.
If := is disliked, how about :: or :::
Also, we could always go with slightly longer names if we want to reduce overlap and keep clarity. alias could be typealias or type alias
typealias is better because quite often I need to have a variable/field named type
I like typealias. Very clear about what it is.
do type alias and opaque type work? Not sure if they should have spaces, or be mashed to gather or other. I think both of those names are clear and due to being longer/2 words should make it so that soft keywords don't feel as strange. opaque, type, and alias used by themselves would be a variable. opaque type and type alias are the only form as a keyword.
that's interesting! Elm uses type alias actually
Ron Panduwana said:
typealiasis better because quite often I need to have a variable/field namedtype
yeah I think in around half the languages I've ever used, I've wanted to use type as a variable name and have been annoyed that it was a reserved keyword :laughing:
a complaint I've heard people say about type alias in Elm is that it's verbose, but I do like that it's super clear
I think if we did type alias it would make sense to also do opaque type
another option is type-alias and opaque-type
From a lexer perspective, I'm worried about hyphenated keywords making things unnecessarily complicated.
Also, hyphenated keywords are pretty unusual I think (outside of lisps, I mean...)
Specifically for anything that we're considering a 'hard' keyword, I'd like to make sure those are always identifiers (by which I mean, they _would_ be valid identifiers if they weren't considered keywords).
And, for 'soft' keywords, they ideally need to be recognizable as such at the parser level from tokens - which is to say, they're still recognized if there's some non-newline whitespace inserted - so opaque-type needs to be tokenized as IDENT, UNARY_MINUS, IDENT - and that in turn means that opaque -type needs to have the same semantics.
(It's definitely _possible_ to do hyphenated keywords in the lexer - but that adds to the list of weird hax that tend to cause problems later)
Does anything else allow hypens in general? I think it would just feel inconsistent with the rest of the language to throw in hypens just for 2 keywords.
Some previous discussion on record syntax
in Roc today:
- always, what comes before = is a pattern
- always, an identifier followed by a : is a record field declaration
- always, an identifier followed by a blank space and then a : is a type declaration
= for both type alias and opaque type# Type annotation
amy : { firstName : Str, lastName : Str }
amy = { firstName: "Amy", lastName: "Lee" }
# Type alias
type alias Musician = { firstName : Str, lastName : Str }
amy : Musician
amy = { firstName: "Amy", lastName: "Lee" }
# Example opaque type definition
opaque type CodePoint = U32
opaque type Json = {}
opaque type Decoder val fmt = List U8, fmt -> DecodeResult val | fmt has DecoderFormatting
# Example type alias
type alias App state initData = {
init : DecodingResult initData -> state,
render : state -> Html state,
wasmUrl : Str,
}
type alias Html state = [
None,
Text Str,
Element Str Size (List (Attribute state)) (List (Html state)),
]
: for both type alias and opaque type# Type annotation
amy : { firstName : Str, lastName : Str }
amy = { firstName: "Amy", lastName: "Lee" }
# Type alias
type alias Musician : { firstName : Str, lastName : Str }
amy : Musician
amy = { firstName: "Amy", lastName: "Lee" }
# Example opaque type definition
opaque type CodePoint : U32
opaque type Json : {}
opaque type Decoder val fmt : List U8, fmt -> DecodeResult val | fmt has DecoderFormatting
# Example type alias
type alias App state initData : {
init : DecodingResult initData -> state,
render : state -> Html state,
wasmUrl : Str,
}
type alias Html state : [
None,
Text Str,
Element Str Size (List (Attribute state)) (List (Html state)),
]
I'm not a fan of the space in between, we already use spaces as separators between arguments so this seems confusingly similar.
My preference would go to typeAlias and just opaque. My reasoning for just opaque is that if you're writing "nice roc" you'll be using Opaque, those using a bool can also simply use isOpaque.
Lean also uses opaque for example
opaque CodePoint : UInt32
opaque Decoder [DecoderFormatting val] : List UInt8 → (fmt → DecodeResult val)
Last updated: Jun 16 2026 at 16:19 UTC