Skip to content

Releases: roc-lang/roc

0.0.0-alpha2-*

29 Jan 18:45
689c58f
Compare
Choose a tag to compare

Note: we're starting with a new release approach. Each time a breaking change happens, the alpha number will be incremented in 0.0.0-alpha. The * indicates a rolling release, the tar.gz archives in assets will be updated if the changes are non-breaking. So upgrading to the latest tar.gz with the same alpha should not create any problems.
We recommend these alpha releases for most users instead of the nightly releases.

❗ if your project used a latest nightly url like:

https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz

You should probably switch it to:

https://github.com/roc-lang/roc/releases/download/0.0.0-alpha2-rolling/roc-linux_x86_64-0-alpha2-rolling.tar.gz

Migration guide

TL;DR

  • Run roc format --migrate to upgrade most syntax changes
  • Replace usages of Task with effectful! functions
  • Use Result.map_ok instead of Result.map
  • Str.BadUtf8 has changed from BadUtf8 Utf8ByteProblem U64 to BadUtf8 { problem: Utf8Problem, index: U64}

What changed?

Since the last update, Roc has gone through lots of syntax changes! We want
to make Roc a friendly language for new programmers, functional programming wizards,
and everyone in between. To that end, we've made Roc look more like mainstream
languages while still keeping things clean and concise.

Let's look at a sample application to see what changed:

app [main] { pf: platform "<basic-cli>" }

import pf.File
import pf.Stdout

main =
    readmePath = "./README.md"
    licensePath = "./LICENSE"

    when checkContentsOfFiles readmePath licensePath |> Task.result! is
        Ok {} -> Task.ok {}
        Err err ->
            msg =
                when err is
                    FileReadErr _ -> "Error reading README"
                    FailedToReadLicense -> "Failed to read LICENSE file"

            Task.err (Exit 1 "unable to read file: $(msg)")

checkContentsOfFiles : Str, Str -> Task {} _
checkContentsOfFiles = \readmePath, licensePath ->
    readmeContents = File.readUtf8! readmePath
    licenseContents = File.readUtf8 licensePath
        |> Task.mapErr! \_ -> FailedToReadLicense

    readmeLines = Str.split readmeContents "\n"
    firstReadmeLine = List.first lines |> Result.withDefault "<empty README>"

    licenseLength = Str.split license "\n" |> List.len

    Stdout.line! "First line of $(readmePath): $(firstReadmeLine)"
    Stdout.line! "Line count for $(licensePath): $(Num.toStr licenseLength)"

This example reads the README and the LICENSE of a repository and prints some info on
it, namely the first line of the README and the number of lines in the LICENSE. Let's
look at its modern counterpart!

app [main] { pf: platform "<basic-cli>" }

import pf.File
import pf.Stdout

main! = |_args|
    readme_path = "./README.md"
    license_path = "./LICENSE"

    check_contents_of_files(readme_path, license_path)
    |> Result.map_err!(|err|
        msg =
            when err is
                FileReadErr(_) -> "Error reading README"
                FailedToReadLicense -> "Failed to read LICENSE file"

        Err(Exit(1, "unable to read file: ${msg}")))

check_contents_of_files! : Str, Str => Result {} _
check_contents_of_files! = |readme_path, license_path|
    readme_contents = File.read_utf8!(readme_path)?
    license_contents = File.read_utf8(license_path) ? |_| FailedToReadLicense

    readme_lines = Str.split(readme_contents, "\n")
    first_readme_line = List.first(lines) ?? "<empty README>"
    license_length = Str.split(license, "\n") |> List.len

    Stdout.line!("First line of ${readme_path}: ${first_readme_line}")?
    Stdout.line!("Line count for ${license_path}: ${Num.to_str(license_length)}")

Lots has changed, even if it works the same under the hood! Let's list the changes:

  • Functions get called with ( and ) now instead of whitespace (also tags like Ok(val) instead of Ok val)
  • Function arguments are now surrounded with | pipes, just like Rust or Ruby
  • All variable and function names are in snake_case instead of camelCase, which makes individual words in variables easier to read
  • Functions that might have side effects (like writing to a file) must have a ! at the end of their name, which means ! is no longer an operator but just a suffix
  • String interpolation now uses ${ and } instead of $( and )
  • We now use and and or for boolean operators instead of the old && and || operators to make them distinct from the new |args| function syntax

In addition to the stylistic changes, there are some additional syntax features for error handling:

  • ? right after an expression that returns a Result will either early return the Err or unwrap the Ok value, just like Rust
  • If you put a space between the expression and ?, it will use the function after the ? to map the Err value first before the early return happens
    • This is really useful for giving additional useful for giving context
  • ?? unwraps a Result with a default value just like Result.with_default(), but with the benefit of only calculating the provided default expression if necessary

Most of the work to change existing Roc code to the new format can be done for you by the Roc compiler; just run

$ roc format --migrate

and the above stylistic changes will get converted for you. The main thing you'll have to do is replace any usage of Task with an effectful function, AKA one with a ! at the end of its name. Task has now been completely removed from Roc, which we think is a great improvement to the simplicity and readability of Roc. Also, Result.map has been renamed to Result.map_ok for better compatability with future plans for Roc.

This has been a whirlwind of changes! Don't worry, our hope has been to do the majority of the changes needed for Roc to look how it will in the long-term, so you won't have to do much migration after this feature push.

Some other APIs we've changed/added:

  • Renamed Result.map with Result.map_ok
  • Introduce some new Str operations:
    • Str.with_ascii_lowercased: make ASCII characters in a string lowercase and leave everything else the same
    • Str.with_ascii_uppercased: make ASCII characters in a string uppercase and leave everything else the same
    • Str.caseless_ascii_equals: compare the contents of two strings, ignoring case for ASCII characters
    • Str.from_utf8: convert a List U16 to a string, failing on invalid codepoints
      • We already had this, but it changed error types:
      • Old: from_utf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem U64]
      • New: from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8Problem, index : U64 }]
    • Str.from_utf8_lossy: convert a List U8 to a string, replacing invalid codepoints with the "�" character
    • Str.from_utf16: convert a List U16 to a string, failing on invalid codepoints
    • Str.from_utf16_lossy: convert a List U16 to a string, replacing invalid codepoints with the "�" character
    • Str.from_utf32: convert a List U16 to a string, failing on invalid codepoints
    • Str.from_utf32_lossy: convert a List U32 to a string, replacing invalid codepoints with the "�" character
  • Added some effectful List walking functions:
    • for_each!: Run an effectful operation on every element in a list
    • for_each_try!: Run an effectful operation on every element in a list, returning early on an Err
    • walk!: Effectfully walk through the elements of a list, building up state as you go
    • walk_try!: Effectfully walk through the elements of a list, building up state as you go, returning early on an Err

That's it for now! Let us know if you run into any issues in the Zulip chat, we're here to help!

0.0.0-alpha1

29 Jan 17:27
a089cf2
Compare
Choose a tag to compare

We're starting with a new release approach, these files will stay available permanently in contrast to the latest nightly.

We now recommend most users to stick to 0.0.0-alpha releases instead of nightly-latest.

This current release is based on commit a089cf2 from the 6th of January 2025. These files are identical to nightly-latest published on the 7th of January 2025.

Nightly build

22 Oct 17:21
9740fff
Compare
Choose a tag to compare
Nightly build Pre-release
Pre-release

🕑 These releases are updated regularly, don't mind the Oct 22, 2021 timestamp. Ask Anton on zulip chat if you need older nightly releases.


⚠️ Common issues:

macOS

You may encounter "roc" can't be opened because Apple cannot check it..., you can fix this by executing xattr -d com.apple.quarantine roc_nightly-macosREST OF FILENAME in the terminal. Or by downloading the file with curl to start: curl -OL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz

NixOS

The roc binary in linux_x86_64 is dynamically linked so you won't be able to use it on NixOS. Alternatively, we recommend you execute nix-build https://github.com/roc-lang/roc/archive/a089cf2.tar.gz, you can then use roc like this ./result/bin/roc repl. If you want to build the language server, execute: nix-build https://github.com/roc-lang/roc/archive/a089cf2.tar.gz -A packages.x86_64-linux.lang-server


👇 Expand "Assets"