Friendly
Besides having a friendly community, Roc also prioritizes being a user-friendly language. This impacts the syntax, semantics, and tools Roc ships with.
Syntax and Formatter
Roc's syntax isn't trivial, but there also isn't much of it to learn. It's designed to be uncluttered and unambiguous. A goal is that you can normally look at a piece of code and quickly get an accurate mental model of what it means, without having to think through several layers of indirection. Here are some examples:
user.emailalways accesses theemailfield of a record nameduser. (Roc has no inheritance, subclassing, or proxying.)Email.isValidalways refers to something namedisValidexported by a module namedEmail. (Module names are always capitalized, and variables/constants never are.) Modules are always defined statically and can't be modified at runtime; there's no monkey patching to consider either.x = doSomething(y, z)always declares a new constantxto be whatever thedoSomethingfunction returns when passed the argumentsyandz."Name: ${name.trim()}"uses string interpolation syntax: a dollar sign inside a string literal, followed by an expression in parentheses.
Roc also ships with a source code formatter that helps you maintain a consistent style with little effort. The roc fmt command neatly formats your source code according to a common style, and it's designed with the time-saving feature of having no configuration options. This feature saves teams all the time they would otherwise spend debating which stylistic tweaks to settle on!
Helpful compiler
Roc's compiler is designed to help you out. It does complete type inference across all your code, and the type system is sound. This means you'll never get a runtime type mismatch if everything type-checked (including null exceptions; Roc doesn't have the billion-dollar mistake), and you also don't have to write any type annotations—the compiler can infer all the types in your program.
If there's a problem at compile time, the compiler is designed to report it in a helpful way. Here's an example:
── TYPE MISMATCH ───────────────────────────────── This expression is used in an unexpected way: ┌─ /.../main.roc:4:9 │ 4 │ if some_decimal > 0 5 │ some_decimal + 1 6 │ else 7 │ 0 It has the type: I64 But you are trying to use it as: Dec
If you like, you can run a program that has compile-time errors like this by using the flag --allow-errors. If the program reaches the error at runtime, it will crash.
This lets you do things like trying out code that's only partially finished, or running tests for one part of your code base while other parts have compile errors. (Note that this feature is only partially completed, and often errors out; it has a ways to go before it works for all compile errors!)
Testing
The roc test command runs a Roc program's tests. Each test is declared with the expect keyword, and can be as short as one line. For example, this is a complete test:
## One plus one should equal two. expect 1 + 1 == 2
If the test fails, roc test will show you the line number of the expect that failed. You can help improve this to make it friendlier!
TODO uncomment once https://github.com/roc-lang/roc/issues/9320 is implemented
If the test fails, roc test will show you the source code of the expect, along with the values of any named variables inside it, so you don't have to separately check what they were.
If you write a documentation comment right before it (like ## One plus one should equal two here), it will appear in the test output, so you can use that to add some descriptive context to the test if you want to.
-->
Functional
Besides being designed to be fast and friendly, Roc is also a functional programming language.