roc/roc-for-elm-programmers.md

1127 lines
42 KiB
Markdown
Raw Normal View History

2019-12-01 07:55:49 +03:00
# Roc for Elm programmers
2021-01-01 07:41:50 +03:00
Roc is a direct descendant of the [Elm programming language](https://elm-lang.org/). The two languages are similar, but not the same!
2019-12-01 07:55:49 +03:00
This is a guide to help Elm programmers learn what's different between Elm and Roc.
2021-01-01 07:41:50 +03:00
> NOTE: As of 2021, most but not all of what's in this document has been implemented.
2019-12-01 07:55:49 +03:00
## Comments
Like [Python](https://www.python.org), [Ruby](https://www.ruby-lang.org), [Perl](https://www.perl.org), and [Elixir](https://elixir-lang.org/), inline comments in Roc begin with `#` instead of `--`.
2019-12-01 07:55:49 +03:00
Doc comments begin with `##` instead.
Like Python, Roc does not have multiline comment syntax.
## String Interpolation
Roc strings work like Elm strings except that they support string interpolation.
Here's a Roc string which uses interpolation:
```elm
"Hi, my name is \(name)!"
```
The Elm equivalent would be:
```elm
"Hi, my name is " ++ name ++ "!"
```
This interpolation syntax comes from [Swift](https://swift.org/). Only a single
identifier can go inside the parentheses (like `(name)` here), and the identifier
needs to be a string already. Arbitrary expressions are not allowed, which means
weird situations like string literals inside string literals do not come up.
Roc strings also have the type `Str` rather than Elm's `String`. This is to make
2021-01-01 07:41:50 +03:00
common qualified operations like `Str.join` more concise; the idea is that you'll use the
2019-12-01 07:55:49 +03:00
abbreviation often enough that you'll quickly get used to it. (I got used to [`str` in
Rust](https://doc.rust-lang.org/std/primitive.str.html) very quickly.)
## Type Aliases
Rather than a `type alias` keyword, in Roc you define type aliases with `:` like so:
```elm
2020-01-04 02:44:15 +03:00
Username : Str
2019-12-01 07:55:49 +03:00
```
You can also define type aliases anywhere, not just at the top level.
Their scoping rules work as normal.
2019-12-01 07:55:49 +03:00
Separately, Roc also allows standalone type annotations with no corresponding implementation.
So I can write this as an annotation with no implementation:
```elm
getUsername : User -> Username
```
2019-12-16 07:42:38 +03:00
Roc will automatically fill in the implementation of this as the equivalent of
2021-01-01 07:41:50 +03:00
a [`Debug.todo`](https://package.elm-lang.org/packages/elm/core/latest/Debug#todo).
If it ever gets run, it will crash, but for debugging purposes or sketching out
APIs, you don't need to bother writing `getUsername = Debug.todo "implement"`.
2019-12-01 07:55:49 +03:00
## `let` syntax
2021-01-01 07:41:50 +03:00
Imagine if Elm's `let`...`in` worked exactly the same way, except you removed
the `let` and `in` keywords. That's how it works in Roc.
2019-12-01 07:55:49 +03:00
For example, this Elm code computes `someNumber` to be `1234`:
```elm
someNumber =
let
foo =
1000
blah =
234
in
foo + blah
```
Here's the equivalent Roc code:
```elm
someNumber =
foo =
1000
blah =
234
foo + blah
```
Like `let`...`in` in Elm, this is indentation-sensitive. Each of the definitions
("defs" for short) must have the same indentation as the ending expression.
## Function definitions
Roc only has one syntax for defining functions, and it looks almost exactly
like Elm's anonymous functions. The one difference is that multiple arguments
are separated by commas.
2019-12-01 07:55:49 +03:00
So where in Elm you might write `foo a b =` in Roc you'd write `foo = \a, b ->` instead.
2021-01-01 07:41:50 +03:00
One minor benefit of the comma is that you don't need to use parentheses to
destructure arguments inline. For example, in Elm, you always need to use parens
to destructure variants inline in function declarations, like in these two examples:
```
\(UserId id1) (UserId id2) ->
\(UserId id) ->
```
Without the parentheses, it wouldn't be clear where one argument ended and the next one began.
In Roc, the commas make argument boundaries unambiguous, so no parens are needed.
You can rewrite the above like so:
```
\UserId id1, UserId id2 ->
\UserId id ->
```
2019-12-01 07:55:49 +03:00
## Unbound type variables
In Elm, every type variable is named. For example:
```
List.reverse : List a -> List a
[] : List a
```
The `a` in `List.reverse` is a *bound* type variable, because it appears more than once in the type.
Whatever the first list's `a` is, that's what the second list's `a` must be as well.
2019-12-16 07:42:38 +03:00
The `a` in `[]` is an *unbound* type variable. It has no restrictions,
2019-12-01 07:55:49 +03:00
which is why `[]` can be passed to any function that expects a `List`.
In Roc, this distinction between bound and unbound type variables is reflected at
the syntax level. Here are those types in Roc:
```
List.reverse : List a -> List a
[] : List *
```
The `*` is the "wildcard" type variable. It is only for unbound type variables like this.
Like the wildcard `*` in path globs like `*.txt`, it matches anything.
2019-12-16 07:42:38 +03:00
> You can still choose names for unbound type variables if you like, but the
> compiler will infer them as `*` by default.
2021-01-01 07:41:50 +03:00
In Elm, the type of `always` is `a -> (b -> a)`. The equivalent Roc type would be:
2019-12-01 07:55:49 +03:00
```elm
always : a -> (* -> a)
```
This makes unbound type variables easier to talk about out loud. Rather than saying
(for example) "List a" or "Html msg with a lowercase m" you can say "List star" or "Html star".
2019-12-16 07:42:38 +03:00
## Pattern matching
2019-12-16 07:42:38 +03:00
Roc's pattern matching conditionals work about the same as how they do in Elm.
Here are two differences:
* Roc uses the syntax `when`...`is` instead of `case`...`of`
* In Roc, you can use `|` to handle multiple patterns in the same way
For example:
```elm
when color is
Blue -> 1
Green | Red | Yellow -> 2
Purple -> 3
```
2019-12-01 07:55:49 +03:00
## Custom Types
2020-05-12 06:39:52 +03:00
This is the biggest semantic difference between Roc and Elm.
Let's start with the motivation. Suppose I'm using a platform for making a
web server, and I want to:
* Read some data from a file
* Send a HTTP request containing some of the data from the file
* Write some data to a file containing some of the data from the HTTP response
Assuming I'm writing this on a Roc platform which has a `Task`-based API,
here's how that code might look:
```elm
doStuff = \filename ->
Task.after (File.read filename) \fileData ->
Task.after (Http.get (urlFromData fileData)) \response ->
2021-01-01 07:41:50 +03:00
File.write filename (responseToData response)
2020-05-12 06:39:52 +03:00
```
A few things to note before getting into how this relates to custom types:
2021-01-01 07:41:50 +03:00
1. This is written in a style designed for chained effects. It resembles [`do` notation](https://en.wikibooks.org/wiki/Haskell/do_notation), but it's implemented as a formatting convention instead of special syntax.
2020-05-12 06:39:52 +03:00
2. In Elm you'd need to add a `<|` before the anonymous functions (e.g. `<| \response ->`) but in Roc you don't. (That parsing design decision was partly motivated by supporting this style of chained effects.)
3. `Task.after` is `Task.andThen` with its arguments flipped.
What would the type of the above expression be? Let's say these function calls
have the following types:
```elm
File.read : Filename -> Task File.Data File.ReadErr
File.write : Filename, File.Data -> Task File.Data File.WriteErr
Http.get : Url -> Task Http.Response Http.Err
2021-01-01 07:41:50 +03:00
Task.after : Task a err, (a -> Task b err) -> Task b err
2020-05-12 06:39:52 +03:00
```
If these are the types, the result would be a type mismatch. Those `Task` values
have incompatible error types, so `after` won't be able to chain them together.
This situation is one of the motivations behind Roc's *tags* feature. Using tags,
not only will this type-check, but at the end we get a combined error type which
has the union of all the possible errors that could have occurred in this sequence.
We can then handle those errors using a single `when`, like so:
2020-08-27 06:36:07 +03:00
```coffeescript
2020-05-12 06:39:52 +03:00
when error is
# Http.Err possibilities
PageNotFound -> ...
Timeout -> ...
BadPayload -> ...
# File.ReadErr possibilities
FileNotFound -> ...
ReadAcessDenied -> ...
FileCorrupted -> ...
# File.WriteErr possibilities
DirectoryNotFound -> ...
WriteAcessDenied -> ...
DiskFull -> ...
```
2020-08-27 06:36:07 +03:00
Here is the set of slightly different types that will make the original chained
expression compile. (`after` is unchanged.)
2020-05-12 06:39:52 +03:00
```elm
File.read : Filename -> Task File.Data (File.ReadErr *)
File.write : Filename, File.Data -> Task File.Data (File.WriteErr *)
Http.get : Url -> Task Http.Response (Http.Err *)
after : Task a err, (a -> Task b err) -> Task b err
```
2020-08-27 06:36:07 +03:00
The key is that each of the error types is a type alias for a Roc *tag union*.
2021-01-01 07:41:50 +03:00
Here's how those look:
2020-05-12 06:39:52 +03:00
```elm
2020-08-27 06:36:07 +03:00
Http.Err a : [ PageNotFound, Timeout, BadPayload ]a
File.ReadErr a : [ FileNotFound, Corrupted, BadFormat ]a
File.WriteErr a : [ FileNotFound, DiskFull ]a
2020-05-12 06:39:52 +03:00
```
2019-12-01 07:55:49 +03:00
2020-08-27 06:36:07 +03:00
In Elm, these would be defined as custom types (aka algebraic data types) using
the `type` keyword. However, instead of traditional algebraic data types, Roc has
only tags - which work more like OCaml's *polymorphic variants*, and which
can be used in type aliases without a separate `type` keyword. (Roc has no `type` keyword.)
2019-12-01 07:55:49 +03:00
2020-08-27 06:36:07 +03:00
Here are some examples of using tags in a REPL:
2019-12-01 07:55:49 +03:00
```
> True
True : [ True ]*
> False
False : [ False ]*
2021-01-01 07:41:50 +03:00
> Ok "hi"
Ok "hi" : [ Ok Str ]*
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
> SomethingIJustMadeUp "hi" "there"
SomethingIJustMadeUp "hi" "there" : [ SomethingIJustMadeUp Str Str ]*
2019-12-01 07:55:49 +03:00
> x = Foo
Foo : [ Foo ]*
2021-01-01 07:41:50 +03:00
> y = Foo "hi" Bar
Foo "hi" 5 : [ Foo Str [ Bar ]* ]*
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
> z = Foo [ "str1", "str2" ]
Foo [ "str1", "str2" ] : [ Foo (List Str) ]*
2019-12-01 07:55:49 +03:00
```
2020-08-27 06:36:07 +03:00
Tags have a lot in common with traditional algebraic data types' *variants*,
but are different in several ways.
2019-12-01 07:55:49 +03:00
One difference is that you can make up any tag you want, on the fly, and use it in any module,
without declaring it first. (These cannot be used to create opaque types; we'll discuss those
in the next section.)
Another difference is that the same tag can be used with different arities and types.
In the REPL above, `x`, `y`, and `z`, can all coexist in the same module even though
they use `Foo` with different arities - and also with different types within the same arity.
2019-12-16 07:42:38 +03:00
Now let's say I do a pattern match with no type annotations.
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
when blah is
MyStr str -> Str.concat str "!"
MyBool bool -> Bool.not bool
2019-12-01 07:55:49 +03:00
```
2021-01-01 07:41:50 +03:00
The inferred type of this expression would be `[ MyStr Str, MyBool Bool ]`,
2019-12-01 07:55:49 +03:00
based on its usage.
2021-01-01 07:41:50 +03:00
> Exhaustiveness checking is still in full effect here. It's based on usage;
> if any code pathways led to `blah` being set to the tag `Foo`, I'd get
> an exhaustiveness error because this `when` does not have a `Foo` branch.
2019-12-01 07:55:49 +03:00
There's an important interaction here between the inferred type of a *when-expression* and
2019-12-01 07:55:49 +03:00
the inferred type of a tag value. Note which types have a `*` and which do not.
```elm
x : [ Foo ]*
x = Foo
2021-01-01 07:41:50 +03:00
y : [ Bar Str ]*
y = Bar "stuff"
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
tagToStr : [ Foo, Bar Str ] -> Str
tagToStr = \tag ->
when tag is
2021-01-01 07:41:50 +03:00
Foo -> "hi"
Bar str -> Str.concat str "!"
2019-12-01 07:55:49 +03:00
```
Each of these type annotations involves a *tag union* - a collection of tags bracketed by `[` and `]`.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
* The type `[ Foo, Bar Str ]` is a **closed** tag union.
2019-12-01 07:55:49 +03:00
* The type `[ Foo ]*` is an **open** tag union.
2021-01-01 07:41:50 +03:00
You can pass `x` to `tagToStr` because an open tag union is type-compatible with
any closed tag union which contains its tags (in this case, the `Foo` tag). You can also
2021-01-01 07:41:50 +03:00
pass `y` to `tagToStr` for the same reason.
2019-12-01 07:55:49 +03:00
In general, when you make a tag value, you'll get an open tag union (with a `*`).
Using `when` *can* get you a closed union (a union without a `*`) but that's not
always what happens. Here's a `when` in which the inferred type is an open tag union:
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
alwaysFoo : [ Foo Str ]* -> [ Foo Str ]*
2019-12-01 07:55:49 +03:00
alwaysFoo = \tag ->
when tag is
2021-01-01 07:41:50 +03:00
Foo str -> Foo (Str.concat str "!")
_ -> Foo ""
2019-12-01 07:55:49 +03:00
```
The return value is an open tag union because all branches return something
tagged with `Foo`.
The argument is also an open tag union, because this *when-expression* has
a default branch; that argument is compatible with any tag union. This means you
2019-12-01 07:55:49 +03:00
can pass the function some totally nonsensical tag, and it will still compile.
> Note that the argument does *not* have the type `*`. That's because you
> cannot pass it values of any type; you can only pass it tags!
>
> You could, if you wanted, change the argument's annotation to be `[]*` and
> it would compile. After all, its default branch means it will accept any tag!
>
2021-01-01 07:41:50 +03:00
> Still, the compiler will infer `[ Foo Str ]*` based on usage.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
Just because `[ Foo Str ]*` is the inferred type of this argument,
2019-12-01 07:55:49 +03:00
doesn't mean you have to accept that much flexibility. You can restrict it
by removing the `*`. For example, if you changed the annotation to this...
```elm
2021-01-01 07:41:50 +03:00
alwaysFoo : [ Foo Str, Bar Bool ] -> [ Foo Str ]*
2019-12-01 07:55:49 +03:00
```
2021-01-01 07:41:50 +03:00
...then the function would only accept tags like `Foo "hi"` and `Bar False`. By writing
out your own annotations, you can get the same level of restriction you get with
traditional algebraic data types (which, after all, come with the requirement that
you write out their annotations). Using annotations, you can restrict even
*when-expressions* with default branches to accept only the values you define to be valid.
2019-12-01 07:55:49 +03:00
In fact, if you want a traditional algebraic data type in Roc, you can get about the same
2019-12-16 07:42:38 +03:00
functionality by making (and then using) a type alias for a closed tag union.
Here's exactly how `Result` is defined using tags in Roc's standard library:
2019-12-01 07:55:49 +03:00
```elm
2019-12-16 07:42:38 +03:00
Result ok err : [ Ok ok, Err err ]
2019-12-01 07:55:49 +03:00
```
2019-12-01 23:48:47 +03:00
You can also use tags to define recursive data structures, because recursive
type aliases are allowed as long as the recursion happens within a tag. For example:
2019-12-01 07:55:49 +03:00
```elm
2019-12-01 23:48:47 +03:00
LinkedList a : [ Nil, Cons a (LinkedList a) ]
2019-12-01 07:55:49 +03:00
```
2019-12-01 23:48:47 +03:00
2021-01-01 07:41:50 +03:00
> Inferred recursive tags use the `as` keyword. For example, the
2019-12-01 23:48:47 +03:00
> inferred version of the above type alias would be:
>
> `[ Nil, Cons a b ] as b`
2019-12-16 07:42:38 +03:00
The `*` in open tag unions is actually an unbound ("wildcard") type variable.
It can be bound too, with a lowercase letter like any other bound type variable.
2019-12-01 07:55:49 +03:00
Here's an example:
```elm
2021-01-01 07:41:50 +03:00
exclaimFoo : [ Foo Str ]a -> [ Foo Str ]a
exclaimFoo = \tag ->
2019-12-16 07:42:38 +03:00
when tag is
2021-01-01 07:41:50 +03:00
Foo str -> Foo (Str.concat str "!")
2019-12-01 07:55:49 +03:00
other -> other
```
The `*` says "this union can also include any other tags", and here the `a` says
2021-01-01 07:41:50 +03:00
"the return value union includes `Foo Str`, plus whichever other tags the argument
2019-12-01 07:55:49 +03:00
includes in its union."
> The Roc type `[]` is equivalent to Elm's `Never`. You can never satisfy it!
## Opaque Types
The tags discussed in the previous section are globally available, which means
they cannot be used to create opaque types.
*Private tags* let you create opaque types. They work just like the *global tags*
from the previous section, except:
* Private tags begin with an `@` (e.g. `@Foo` instead of `Foo`)
* Private tags are scoped to the current module, rather than globally scoped
* Private tags can only be instantiated in the current module
2021-01-01 07:41:50 +03:00
For example, suppose I define these inside the `Username` module:
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
Username : [ @Username Str ]
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
fromStr : Str -> Username
fromStr = \str ->
@Username str
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
toStr : Username -> Str
toStr = \@Username str ->
str
2019-12-01 07:55:49 +03:00
```
2021-01-01 07:41:50 +03:00
I can now expose the `Username` type alias, which other modules can use as an opaque type.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
It's not even syntactically possible for me to expose the `@Username` tag,
because `@` tags are not allowed in the exposing list. Only code written in this
`Username` module can instantiate a `@Username` value.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
> If I were to write `@Username` inside another module (e.g. `Main`), it would compile,
> but that `@Username` would be type-incompatible with one created inside the `Username` module.
2019-12-01 07:55:49 +03:00
> Even trying to use `==` on them would be a type mismatch, because I would be comparing
2021-01-01 07:41:50 +03:00
> a `[ Username.@Username Str ]*` with a `[ Main.@Username Str ]*`, which are incompatible.
2019-12-01 07:55:49 +03:00
## Modules and Shadowing
In Elm, my main module (where `main` lives) might begin like this:
2019-12-01 07:55:49 +03:00
```elm
module MyApp exposing (main)
import Parser
import Http exposing (Request)
import Task exposing (Task, after)
```
Roc application modules (where the equivalent of `main` lives) begin with the
`app` keyword rather than the `module` keyword, and the import syntax is a bit different.
2021-01-01 07:41:50 +03:00
Here's how the above module header imports section would look in Roc:
```elm
app imports [ Parser, Http.{ Request }, Task.{ Task, after } ]
```
2019-12-01 07:55:49 +03:00
`app` modules are application entrypoints, and they don't formally expose anything.
They also don't have names, so other modules can't even import them!
Modules that *can* be imported are `interface` modules. Their headers look like this:
```elm
interface Parser
exposes [ Parser, map, oneOf, parse ]
imports [ Utf8 ]
```
The name `interface` is intended to draw attention to the fact that the interface
2019-12-16 07:42:38 +03:00
these expose is very important.
2019-12-01 07:55:49 +03:00
All imports and exports in Roc are enumerated explicitly; there is no `..` syntax.
> Since neither global tags nor private tags have a notion of "importing variants"
> (global tags are always available in all modules, and private tags are
> never available in other modules), there's also no `exposing (Foo(..))` equivalent.
2019-12-01 07:55:49 +03:00
Like Elm, Roc does not allow shadowing.
Elm does permit overriding open imports - e.g. if you have
`import Foo exposing (bar)`, or `import Foo exposing (..)`, you can still define
2021-01-01 07:41:50 +03:00
`bar = ...` in the module. Roc treats this as shadowing and does not allow it.
2019-12-01 07:55:49 +03:00
## Record Syntax
Roc uses Rust/JavaScript syntax for record literals, e.g. `{ x: 1, y: 2 }`.
It also allows omitting the value; `{ x, y }` is sugar for `{ x: x, y: y }`.
You can pattern match on exact record values, e.g. `{ x: 5 } ->`.
2019-12-16 07:42:38 +03:00
Roc does not have the "a type alias for a record creates a convenience constructor function"
feature that Elm has. This is partly because `Point x y` is already defined to be tag application
2019-12-01 07:55:49 +03:00
in Roc, but also because `{ x, y }` would be the recommended way to write it regardless.
Closed record annotations look the same as they do in Elm, e.g.
2021-01-01 07:41:50 +03:00
`{ name : Str, email : Str }`. Open record annotations look a bit different.
2019-12-01 07:55:49 +03:00
In Elm:
```
2021-01-01 07:41:50 +03:00
{ a | name : Str, email : Str } -> Str
2019-12-01 07:55:49 +03:00
```
In Roc:
```
2021-01-01 07:41:50 +03:00
{ x : name : Str, email : Str }* -> Str
2019-12-01 07:55:49 +03:00
```
Here, the open record's type variable appears attached to the `}`.
> In the Elm example, the `a` is unbound, which in Roc means it appears as `*`.
2020-09-06 20:24:04 +03:00
Here's how that looks with a bound type variable. In Elm:
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
{ a | name : Str, email : Str } -> { a | name : Str, email : Str }
2019-12-01 07:55:49 +03:00
```
In Roc:
```elm
2021-01-01 07:41:50 +03:00
{ name : Str, email : Str }a -> { name : Str, email : Str }a
2019-12-01 07:55:49 +03:00
```
2019-12-16 07:42:38 +03:00
By design, this syntax makes the unbound case look natural and the bound case look unnatural.
2019-12-01 07:55:49 +03:00
That's because writing a function that accepts an open record with an unbound
type variable (e.g. "this record, plus other fields if you like") is a totally reasonable
thing to do - as often as you like! It has multiple upsides: it makes "named arguments"
work with data model records more often, and makes it easier to change functions in
backwards-compatible ways. It has no major downsides.
2019-12-01 07:55:49 +03:00
The syntax encourages doing this. "Just add a star" like so:
```elm
2021-01-01 07:41:50 +03:00
{ name : Str, email : Str }* -> Str
2019-12-01 07:55:49 +03:00
```
2019-12-16 07:42:38 +03:00
In contrast, using records with bound variables should be extremely rare.
2019-12-01 07:55:49 +03:00
2019-12-16 07:42:38 +03:00
They need to exist for the type system to work, and they aren't *useless*,
2021-01-01 07:41:50 +03:00
but if you find yourself reaching for them, there is a very high chance
that there's a better way to write that code.
2019-12-01 07:55:49 +03:00
The unnatural-looking syntax is the language's way of nudging you to reconsider,
to search a little further for a better way to express things.
2020-01-05 06:42:57 +03:00
## Record Update
2019-12-01 07:55:49 +03:00
Elm has "record update" syntax like this:
```elm
{ user | firstName = "Sam", lastName = "Sample" }
```
2020-01-05 06:42:57 +03:00
Roc has the same feature, but its syntax looks like this:
2019-12-01 07:55:49 +03:00
```elm
2020-01-05 06:42:57 +03:00
{ user & firstName: "Sam", lastName: "Sample" }
2019-12-01 07:55:49 +03:00
```
2020-01-05 06:42:57 +03:00
The record before the `&` can be qualified, like so:
2019-12-01 07:55:49 +03:00
2020-01-05 06:42:57 +03:00
```elm
{ Foo.defaultConfig & timeZone: utc }
2019-12-01 07:55:49 +03:00
```
2020-01-05 06:42:57 +03:00
However, it cannot involve record field access. So this would *not* compile:
2019-12-01 07:55:49 +03:00
```elm
2020-01-05 06:42:57 +03:00
{ Foo.defaults.config & timeZone: utc }
2019-12-01 07:55:49 +03:00
```
2020-08-01 22:42:03 +03:00
## Optional Record Fields
2020-08-04 01:50:50 +03:00
There's a pattern in Elm where you pass a function a record of configuration
values, some of which you don't really care about and want to leave as defaults.
To incorporate the default config options, you call the function like so:
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
```elm
table { defaultConfig | height = 800, width = 600 }
```
This way, as the caller I'm specifying only the `height` and `width` fields,
and leaving the others to whatever is inside `defaultConfig`. Perhaps it also
has the fields `x` and `y`.
In Roc, you can do this like so:
```elm
2020-09-21 21:33:25 +03:00
table { height: 800, width: 600 }
2020-08-04 01:50:50 +03:00
```
...and the `table` function will fill in its default values for `x` and `y`.
There is no need to use a `defaultConfig` record.
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
Here's how `table` would be defined in Roc:
```
2021-01-01 07:41:50 +03:00
table = \{ height, width, x ? 0, y ? 0 } ->
2020-08-04 01:50:50 +03:00
```
This is using *optional field destructuring* to destructure a record while
also providing default values for any fields that might be missing.
Here's the type of `table`:
```
2021-01-01 07:41:50 +03:00
table : { height : Pixels, width : Pixels, x ? Pixels, y ? Pixels } -> Table
table = \{ height, width, x ? 0, y ? 0 } ->
2020-08-04 01:50:50 +03:00
```
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
This says that `table` takes a record with two *required* fields (`height` and
`width` and two *optional* fields (`x` and `y`). It also says that all of those
2021-01-01 07:41:50 +03:00
fields have the type `Pixels` This means you can choose to omit `x`, `y`, or both,
when calling the function...but if you provide them, they must have the type `Pixels`.
(`Pixels` ia a type alias for a numeric type here.)
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
This is also the type that would have been inferred for `table` if no annotation
had been written. Roc's compiler can tell from the destructuring syntax
2021-01-01 07:41:50 +03:00
`x ? 0` that `x` is an optional field, and that it has the type `Pixels`. These
2020-08-04 01:50:50 +03:00
default values can reference other expressions in the record destructure; if you
2021-01-01 07:41:50 +03:00
wanted, you could write `{ height, width, x ? 0, y ? x + 1 }`.
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
Destructuring is the only way to implement a record with optional fields.
(For example, if you write the expression `config.x` and `x` is an optional field,
you'll get a compile error.)
2020-08-01 22:42:03 +03:00
2020-08-04 01:50:50 +03:00
This means it's never possible to end up with an "optional value" that exists
outside a record field. Optionality is a concept that exists only in record fields,
and it's intended for the use case of config records like this. The ergonomics
of destructuring mean this wouldn't be a good fit for data modeling.
2020-08-01 22:42:03 +03:00
## Function equality
In Elm, if you write `(\val -> val) == (\val -> val)`, you currently get a runtime exception
which links to [the `==` docs](https://package.elm-lang.org/packages/elm/core/latest/Basics#==),
which explain why this is the current behavior and what the better version will look like.
> OCaml also has the "runtime exception if you compare functions for structural equality"
> behavior, but unlike Elm, in OCaml this appears to be the long-term design.
In Roc, function equality is a compile error, tracked explicitly in the type system.
Here's the type of Roc's equality function:
```elm
'val, 'val -> Bool
```
Whenever a named type variable in Roc has a `'` at the beginning, that means
it is a *functionless* type - a type which cannot involve functions.
2021-01-01 07:41:50 +03:00
If there are any functions in that type, you get a type mismatch. This is true
whether `val` itself is a function, or if it's a type that wraps a function,
2021-01-01 07:41:50 +03:00
like `{ predicate: (Str -> Bool) }` or `List (Bool -> Bool)`.
So if you write `(\a -> a) == (\a -> a)` in Roc, you'll get a type mismatch.
If you wrap both sides of that `==` in a record or list, you'll still get a
type mismatch.
If a named type variable has a `'` anywhere in a given type, then it must have a `'`
everywhere in that type. So it would be an error to have a type like `x, 'x -> Bool`
because `x` has a `'` in one place but not everywhere.
## Standard Data Structures
2019-12-01 07:55:49 +03:00
Elm has `List`, `Array`, `Set`, and `Dict` in the standard library.
2021-01-01 07:41:50 +03:00
Roc has all of these except `Array`, and there are some differences in how they work:
2019-12-01 07:55:49 +03:00
2020-09-06 20:24:04 +03:00
* `List` in Roc uses the term "list" the way Python does: to mean an ordered sequence of elements. Roc's `List` is more like an array, in that all the elements are sequential in memory and can be accessed in constant time. It still uses the `[` `]` syntax for list literals. Also there is no `::` operator because "cons" is not an efficient operation on an array like it is in a linked list.
2021-01-01 07:41:50 +03:00
* `Set` in Roc is like `Set` in Elm: it's shorthand for a `Dict` with keys but no value, and it has a slightly different API.
* `Dict` in Roc is like `Dict` in Elm, except it's backed by hashing rather than ordering. Roc silently computes hash values for any value that can be used with `==`, so instead of a `comparable` constraint on `Set` elements and `Dict` keys, in Roc they instead have the *functionless* constraint indicated with a `'`.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
Roc also has a literal syntax for dictionaries and sets. Here's how to write a `Dict` literal:
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
{: "Sam" => True, "Ali" => False, firstName => False :}
```
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
This expression has the type `Dict Str Bool`, and the `firstName` variable would
necessarily be a `Str` as well.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
The `Dict` literal syntax is for two reasons. First, Roc doesn't have tuples;
without tuples, initializing the above `Dict` would involve an API that looked
something like one of these:
```elm
2021-01-01 07:41:50 +03:00
Dict.fromList [ { k: "Sam", v: True }, { k: "Ali", v: False }, { k: firstName, v: False } ]
2021-01-01 07:41:50 +03:00
Dict.fromList [ KV "Sam" True, KV "Ali" False KV firstName False
```
This works, but is not nearly as nice to read.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
Additionally, map literals can compile direcly to efficient initialization code
without needing to (hopefully be able to) optimize away the intermediate
`List` involved in `fromList`.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
`{::}` is an empty `Dict`.
You can write a `Set` literal like this:
```elm
2020-08-14 15:28:22 +03:00
[: "Sam", "Ali", firstName :]
```
2020-08-14 15:28:22 +03:00
2021-01-01 07:41:50 +03:00
The `Set` literal syntax is partly for the initialization benefit, and also
for symmetry with the `Dict` literal syntax.
2020-08-14 15:28:22 +03:00
`[::]` is an empty `Set`.
2019-12-01 07:55:49 +03:00
Roc does not have syntax for pattern matching on data structures - not even `[` `]` like Elm does.
## Operators
2019-12-16 07:42:38 +03:00
In Elm, operators are functions. In Roc, all operators are syntax sugar.
This means, for example, that you cannot write `(/)` in Roc; that would be a syntax
2020-06-23 04:58:42 +03:00
error. However, the `/` operator in Roc is infix syntax sugar for `Num.div`,
2019-12-01 07:55:49 +03:00
which is a normal function you can pass to anything you like.
Elm has one unary operator, namely `-`. (In Elm, `-x` means
"apply unary `negate` to `x`.") Roc has that one, and also unary `!`.
2021-01-01 07:41:50 +03:00
The expression `!foo` desugars to `Bool.not foo`, and `!foo bar` desugars
to `Bool.not (foo bar)`.
2019-12-01 07:55:49 +03:00
2019-12-16 07:42:38 +03:00
This was introduced because Roc does not expose any functions globally by default
(the way Elm does with `Basics` functions like `not`, `round`, etc.).
2021-01-01 07:41:50 +03:00
In Roc, only operators and standard types (like `Str` and `Bool`) are exposed globally.
2019-12-01 07:55:49 +03:00
Having to fully qualify `not` was annoying, and making an exception just for `not` seemed
2019-12-16 07:42:38 +03:00
less appealing than making an operator for it, especially when unary `!` is so widely used
in other languages.
2019-12-01 07:55:49 +03:00
2019-12-16 07:42:38 +03:00
Because Roc has unary `!`, its "not equal to" operator is `!=` instead of Elm's `/=`,
2019-12-01 07:55:49 +03:00
for symmetry with unary `!`.
There's an Operator Desugaring Table at the end of this guide, so you can see exactly
what each Roc operator desugars to.
## The `<|` operator
Roc has no `<|` operator. (It does have `|>` though.)
2019-12-01 07:55:49 +03:00
In Elm, `<|` is used as a minor convenience for when you want to avoid some parens
in a single-line expression (e.g. `foo <| bar baz` over `foo (bar baz)`) and as
a major convenience when you want to pass an anonymous function, `if`, or `case` as an argument.
For example, `elm-test` relies on it:
```elm
test "it works" <|
\_ -> ...
```
In Roc, this does not require a `<|`. This Roc code does the same thing as the preceding Elm code:
```elm
test "it works"
\_ -> ...
```
You don't need parens or an operator to pass an anonymous function, `when`, or `if` as arguments. Here's another example:
2019-12-01 07:55:49 +03:00
```elixir
foo 1 2 if something then 3 else 4
# Same as `foo 1 2 (if something then 3 else 4)`
```
2019-12-16 07:42:38 +03:00
[CoffeeScript](http://coffeescript.org/) also does this the way Roc does.
2019-12-01 07:55:49 +03:00
## Currying and `|>`
2019-12-16 07:42:38 +03:00
Roc functions aren't curried. Calling `(List.append foo)` is a type mismatch
because `List.append` takes 2 arguments, not 1.
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
For this reason, function type annotations separate arguments with `,` instead of `->`.
In Roc, the type of `Set.add` is:
2019-12-01 07:55:49 +03:00
```elm
2021-01-01 07:41:50 +03:00
Set.add : Set 'elem, 'elem -> Set 'elem
2019-12-01 07:55:49 +03:00
```
2021-01-01 07:41:50 +03:00
You might notice that Roc's `Set.add` takes its arguments in the reverse order
from how they are in Elm; the `Set` is the first argument in Roc, whereas it would
2019-12-16 07:42:38 +03:00
be the last argument in Elm. This is because Roc's `|>` operator works like Elixir's
2019-12-01 07:55:49 +03:00
rather than like Elm's; here is an example of what it does in Roc:
```elixir
a b c
|> f x y
# f (a b c) x y
```
In Roc, the `|>` operator inserts the previous expression as the *first* argument
to the subsequent expression, rather than as the *last* argument as it does in Elm.
2020-06-23 04:58:42 +03:00
This makes a number of operations more useful in pipelines. For example, in Roc, `|> Num.div 2.0` divides by 2:
2019-12-01 07:55:49 +03:00
```elixir
2000
2020-06-23 04:58:42 +03:00
|> Num.div 2.0
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
# 1000.0
2019-12-01 07:55:49 +03:00
```
In Elm, where `|>` inserts 2 as the last argument, 2 ends up being the *numerator*
rather than the denominator:
2019-12-01 07:55:49 +03:00
```elm
2000
|> (/) 2.0
2021-01-01 07:41:50 +03:00
# 0.001
2019-12-01 07:55:49 +03:00
```
2021-01-01 07:41:50 +03:00
Another example is `List.append`, which is called `List.concat` in Roc:
2019-12-01 07:55:49 +03:00
```elixir
[ 1, 2 ]
2021-01-01 07:41:50 +03:00
|> List.concat [ 3, 4 ]
2019-12-01 07:55:49 +03:00
# [ 1, 2, 3, 4 ]
```
In Elm:
```elm
[ 1, 2 ]
|> List.append [ 3, 4 ]
# [ 3, 4, 1, 2 ]
```
2019-12-16 07:42:38 +03:00
> There are various trade-offs here, of course. Elm's `|>` has a [very elegant implementation](https://github.com/elm/core/blob/665624859a7a432107059411737e16d1d5cb6373/src/Basics.elm#L873-L874), and `(|>)` in Elm can be usefully passed to other
2019-12-01 07:55:49 +03:00
> functions (e.g. `fold`) whereas in Roc it's not even possible to express the type of `|>`.
As a consequence of `|>` working differently, "pipe-friendly" argument ordering is also
2021-01-01 07:41:50 +03:00
different. That's why `Set.add` has a "flipped" signature in Roc; otherwise, `|> Set.add 5` wouldn't work. Here's the type of Roc's `Set.add` again, and also a pipeline using it:
2019-12-01 07:55:49 +03:00
2019-12-16 07:42:38 +03:00
```coffeescript
2021-01-01 07:41:50 +03:00
Set.add : Set 'elem, 'elem -> Set 'elem
2019-12-01 07:55:49 +03:00
2021-01-01 07:41:50 +03:00
[: "a", "b", "c" :]
|> Set.add "d"
2019-12-01 07:55:49 +03:00
```
Roc has no `<<` or `>>` operators, and there are no functions in the standard library
for general-purpose pointfree function composition.
2021-01-01 07:41:50 +03:00
## Numbers
Like Elm, Roc organizes numbers into integers and floating-point numbers.
However, Roc breaks them down even further. For example, Roc has two different
sizes of float types to choose from:
* `F64` - a 64-bit [IEEE 754 binary floating point number](https://en.wikipedia.org/wiki/IEEE_754#Binary)
* `F32` - a 32-bit [IEEE 754 binary floating point number](https://en.wikipedia.org/wiki/IEEE_754#Binary)
Both types are desirable in different situations. For example, when doing
simulations, the precision of the `F64` type is desirable. On the other hand,
GPUs tend to heavily prefer 32-bit floats because a serious bottleneck is how
long it takes data to transfer from CPU to GPU, so having to send half as many
bytes per render (compared to 64-bit floats) can be huge for performance.
Roc also supports `D64` and `D32`, which are [IEEE 754 decimal floating point numbers](https://en.wikipedia.org/wiki/IEEE_754#Decimal). The upside of these is that they are decimal-based, so
`0.1 + 0.2 == 0.3` (whereas in binary floats [this is not true](https://0.30000000000000004.com/)),
which makes them much better for calculations involving currency, among others.
The downside of decimal floats is that they do not have hardware support
(except on certain [highly uncommon processors](https://en.wikipedia.org/wiki/IEEE_754#See_also)),
so calculations involving them take longer.
Roc does not let floating point calculations result in `Infinity`, `-Infinity`,
or `NaN`. Any operation which would result in one of these
(such as `sqrt` or `/`) will return a `Result`.
Similarly to how there are different sizes of floating point numbers,
there are also different sizes of integer to choose from:
* `I8`
* `I16`
* `I32`
* `I64`
* `I128`
Roc also has *unsigned* integers which are never negative. They are
`U8`, `U16`, `U32`, `U64`, `U128`, and `Nat`.
The size of `Nat` depends on what target you're building for; on a 64-bit target
(the most common), at runtime `Nat` will be the same as `U64`, whereas on a 32-bit
target (for example, WebAssembly) at runtime it will be the same as `U32` instead.
`Nat` comes up most often with collection lengths and indexing into collections.
For example:
* `List.len : List * -> Nat`
* `List.get : List elem, Nat -> List elem`
* `List.set : List elem, Nat, elem -> List elem`
As with floats, which integer type to use depends on the values you want to support
as well as your performance needs. For example, raw sequences of bytes are typically
represented in Roc as `List U8`. You could also represent them as `List U128`,
but it's much more efficient to use `List U8`, since each byte will be at most 255 anyway.
Like Elm, it's possible in Roc to have functions that work on either integers
or floating-point numbers. However, the types are different. For example,
the type of `Num.add` (which the `+` operator desugars to) is:
```elm
Num.add : Num a, Num a -> Num a
```
This accepts any of the numeric types discussed above, from `I128` to `F32`
to `D64` and everything in between. This is because those are all type aliases
for `Num` types. For example:
* `I64` is a type alias for `Num (Integer Signed64)`
* `U8` is a type alias for `Num (Integer Unsigned8)`
* `F32` is a type alias for `Num (FloatingPoint Binary32)`
* `D64` is a type alias for `Num (FloatingPoint Decimal64)`
(Those types like `Integer`, `FloatingPoint`, and `Signed64` are all defined like `Never`; you can never instantiate one.
They are used only as phantom types.)
So Roc does not use `number`, but rather uses `Num` - which works more like `List`.
Either way, you get `+` being able to work on both integers and floats!
Separately, there's also `Int a`, which is a type alias for `Num (Integer a)`,
and `Float a`, which is a type alias for `Num (Float a)`. These allow functions
that can work on any integer or any float. For example,
2021-01-01 07:41:50 +03:00
`Num.bitwiseAnd : Int a, Int a -> Int a`.
In Roc, number literals with decimal points are `Float *` values.
Number literals *without* a decimal point are `Num *` values. Almost always these
will end up becoming something more specific, but in the unlikely event
(most often in a REPL) that you actually do end up with an operation that runs
on either an `Int *` or a `Num *` value, it will default to being treated as
an `I64`. Similarly, a `Float *` value will default to being treated as a `D64`,
which means if someone is learning Roc as their first programming language and
they type `0.1 + 0.2` into a REPL, they won't be confused by the answer.
If you encounter overflow with either integers or floats in Roc, you get a runtime
exception rather than wrapping overflow behavior (or a float becoming `Infinity`
or `-Infinity`). You can opt into wrapping overflow instead with functions like
`Num.addWrap : Int a, Int a -> Int a`, or use a function that gives `Err` if it
overflows, like `Num.addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*`.
## `comparable`, `appendable`, and `number`
These don't exist in Roc.
* `appendable` is only used in Elm for the `(++)` operator, and Roc doesn't have that operator.
* `comparable` is used for comparison operators (like `<` and such), plus `List.sort`, `Dict`, and `Set`. Roc's `List.sort` accepts a "sorting function" argument which specifies how to sort the elements. Roc's comparison operators (like `<`) only accept numbers; `"foo" < "bar"` is valid Elm, but will not compile in Roc. Roc's dictionaries and sets are hashmaps behind the scenes (rather than ordered trees), and their keys only need the functionless restriction.
* `number` is replaced by `Num`, as described previously.
Also [like Python](https://www.python.org/dev/peps/pep-0515/)
Roc permits underscores in number literals for readability purposes. Roc also supports
hexadecimal (`0x01`), octal (`0o01`), and binary (`0b01`) integer literals; these
literals all have type `Int *` instead of `Num *`.
2021-01-01 07:41:50 +03:00
If you put these into a hypothetical Roc REPL, here's what you'd see:
```elm
> 1_024 + 1_024
2048 : Num *
> 1 + 2.14
3.14 : Float *
2021-01-01 07:41:50 +03:00
> 1.0 + 1
2.0 : Float *
2021-01-01 07:41:50 +03:00
> 1.1 + 0x11
<type mismatch between `1.1 : Float *` and `0x11 : Int *`>
2021-01-01 07:41:50 +03:00
> 11 + 0x11
28 : Int *
2021-01-01 07:41:50 +03:00
```
## Phantom Types
[Phantom types](https://medium.com/@ckoster22/advanced-types-in-elm-phantom-types-808044c5946d)
exist in Elm but not in Roc. This is because phantom types can't be defined
using type aliases (in fact, there is a custom error message in Elm if you
try to do this), and Roc only has type aliases. However, in Roc, you can achieve
the same API and runtime performance characteristics as if you had phantom types,
by using *phantom values* instead.
A phantom value is one which affects types, but which holds no information at runtime.
As an example, let's say I wanted to define a [units library](https://package.elm-lang.org/packages/ianmackenzie/elm-units/latest/) -
a classic example of phantom types. I could do that in Roc like this:
```
Quantity units data : [ Quantity units data ]
km : Num a -> Quantity [ Km ] (Num a)
km = \num ->
Quantity Km num
cm : Num a -> Quantity [ Cm ] (Num a)
cm = \num ->
Quantity Cm num
mm : Num a -> Quantity [ Mm ] (Num a)
mm = \num ->
Quantity Mm num
add : Quantity u (Num a), Quantity u (Num a) -> Quantity u (Num a)
add = \Quantity units a, Quantity _ b ->
Quantity units (a + b)
```
From a performance perspective, it's relevant here that `[ Km ]`, `[ Cm ]`, and `[ Mm ]`
are all unions containing a single tag. That means they hold no information at runtime
(they would always destructure to the same tag), which means they can be "unboxed" away -
that is, discarded prior to code generation.
During code generation, Roc treats `Quantity [ Km ] Int` as equivalent to `Quantity Int`.
Then, becaue `Quantity Int` is an alias for `[ Quantity Int ]`, it will unbox again
and reduce that all the way down to to `Int`.
This means that, just like phantom *types*, phantom *values* affect type checking
only, and have no runtime overhead. Rust has a related concept called [phantom data](https://doc.rust-lang.org/nomicon/phantom-data.html).
2019-12-01 18:13:20 +03:00
## Standard library
`elm/core` has these modules:
* `Array`
* `Basics`
* `Bitwise`
* `Char`
* `Debug`
* `Dict`
* `List`
* `Maybe`
* `Platform`
* `Platform.Cmd`
* `Platform.Sub`
* `Process`
* `Result`
* `Set`
* `String`
* `Task`
* `Tuple`
In Roc, the standard library is not a standalone package. It is baked into the compiler,
and you can't upgrade it independently of a compiler release; whatever version of
Roc you're using, that's the version of the standard library you're using too.
(This is because Roc doesn't have a concept like Elm's `Kernel`; it would not be
2019-12-01 18:13:20 +03:00
possible to ship Roc's standard library as a separate package!)
Roc's standard library has these modules:
2021-01-01 07:41:50 +03:00
* `Str`
2019-12-01 18:13:20 +03:00
* `Bool`
2020-05-12 06:39:52 +03:00
* `Num`
2019-12-01 18:13:20 +03:00
* `List`
2021-01-01 07:41:50 +03:00
* `Dict`
* `Set`
* `Result`
2019-12-01 18:13:20 +03:00
Some differences to note:
2021-01-01 07:41:50 +03:00
* All these standard modules are imported by default into every module. They also expose all their types (e.g. `Bool`, `List`, `Result`) but they do not expose any values - not even `negate` or `not`. (`True`, `False`, `Ok`, and `Err` are all global tags, so they do not need to be exposed; they are globally available regardless!)
2019-12-01 18:13:20 +03:00
* In Roc it's called `Str` instead of `String`.
2021-01-01 07:41:50 +03:00
* `List` refers to something more like Elm's `Array`, as noted earlier.
* No `Char`. This is by design. What most people think of as a "character" is a rendered glyph. However, rendered glyphs are comprised of [grapheme clusters](https://stackoverflow.com/a/27331885), which are a variable number of Unicode code points - and there's no upper bound on how many code points there can be in a single cluster. In a world of emoji, I think this makes `Char` error-prone and it's better to have `Str` be the smallest indivisible unit. If you want to iterate over grapheme clusters, use a `Str -> List Str` function which breaks the string down on grapheme boundaries. For this reason there also isn't a `Str.length` function; in the context of strings, "length" is ambiguous. (Does it refer to number of bytes? Number of Unicode code points? Number of graphemes?)
* No `Basics`. You use everything from the standard library fully-qualified; e.g. `Bool.not` or `Num.negate` or `Num.ceiling`. There is no `Never` because `[]` already serves that purpose. (Roc's standard library doesn't include an equivalent of `Basics.never`, but it's one line of code and anyone can implmement it: `never = \a -> never a`.)
* No `Tuple`. Roc doesn't have tuple syntax. As a convention, `Pair` can be used to represent tuples (e.g. `List.zip : List a, List b -> List [ Pair a b ]*`), but this comes up infrequently compared to languages that have dedicated syntax for it.
* No `Task`. By design, platform authors implement `Task` (or don't; it's up to them) - it's not something that really *could* be usefully present in Roc's standard library.
* No `Process`, `Platform`, `Cmd`, or `Sub` - similarly to `Task`, these are things platform authors would include, or not.
* No `Maybe`. This is by design. If a function returns a potential error, use `Result` with an error type that uses a zero-arg tag to describe what went wrong. (For example, `List.first : List a -> Result a [ ListWasEmpty ]*` instead of `List.first : List a -> Maybe a`.) If you want to have a record field be optional, use an Optional Record Field directly (see earlier). If you want to describe something that's neither an operation that can fail nor an optional field, use a more descriptive tag - e.g. for a nullable JSON decoder, instead of `nullable : Decoder a -> Decoder (Maybe a)`, make a self-documenting API like `nullable : Decoder a -> Decoder [ Null, NonNull a ]*`.
2019-12-01 18:13:20 +03:00
2019-12-01 07:55:49 +03:00
## Operator Desugaring Table
2019-12-01 18:21:22 +03:00
Here are various Roc expressions involving operators, and what they desugar to.
2019-12-01 23:37:16 +03:00
| Expression | Desugars to |
| --------------- | ---------------- |
| `a + b` | `Num.add a b` |
| `a - b` | `Num.sub a b` |
| `a * b` | `Num.mul a b` |
2020-06-23 04:58:42 +03:00
| `a / b` | `Num.div a b` |
| `a // b` | `Num.divFloor a b` |
2019-12-01 23:37:16 +03:00
| `a ^ b` | `Num.pow a b` |
2020-06-23 04:58:42 +03:00
| `a % b` | `Num.rem a b` |
| `a %% b` | `Num.mod a b` |
2019-12-01 23:37:16 +03:00
| `-a` | `Num.neg a` |
| `-f x y` | `Num.neg (f x y)` |
| `a == b` | `Bool.isEq a b` |
| `a != b` | `Bool.isNotEq a b` |
| `a && b` | `Bool.and a b` |
| `a \|\| b` | `Bool.or a b` |
| `!a` | `Bool.not a` |
| `!f x y` | `Bool.not (f x y)` |
| `a \|> b` | `b a` |
| `a b c \|> f x y` | `f (a b c) x y` |