Releases: roc-lang/roc
alpha4-*
🆕 Additions
- builtin
List.keep_if_try!to dokeep_ifwith an effectful function #7804 - builtins
Num.[f32,f64,dec]_[to,from]_bits#7741 Str.lento guide Roc users :) #7732
🗑️ Deprecated
Num.[f32,f64]_[to,from]_parts#7741
🐛 Fixes
roc checkon file that isn't app #7870- Language server crash when downloading something that's not in the cache #8237
- Language server stack overflow #8204
- Alias
Str.Utf8Problemnot registered in delayed aliases #8182 not a tag union: Alias(...#8107- Correct exit code when expect fails with
roc somefile.roc#7767 roc format --migratefixes #7716- Tutorial copy button #8077
- Fix undefined behavior in canonicalization #7835
- Fix broken links in docs #8235 813709b 6c09f23 8bd4f20
✨ Improvements
- Massive build speed improvement when using many
?#7884 - Better AI docs for packages, builtins, and platforms #8131
- Better Result docs #8220
roc buildno longer tries to run if the check phase did not pass #7784- Nicer error tip on undefined symbol #7913
Other Notable Changes
- Website has moved to its own repo
alpha3-*
Breaking Changes
- Both macos releases are now built with macos 13 so they may no longer work on macos 12.
- old_linux releases are gone, they were built with really outdated software at this point.
0-was removed from the filename of the tar.gz archives.0.0.0-was removed from the tag name; it is nowalpha3-rolling
Other Changes
- The roc binaries built for macos no longer require the z3 dependency.
brew install zstdshould be all that is required.
0.0.0-alpha2-*
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 --migrateto upgrade most syntax changes - Replace usages of
Taskwitheffectful!functions - Use
Result.map_okinstead ofResult.map Str.BadUtf8has changed fromBadUtf8 Utf8ByteProblem U64toBadUtf8 { 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 likeOk(val)instead ofOk val) - Function arguments are now surrounded with
|pipes, just like Rust or Ruby - All variable and function names are in
snake_caseinstead ofcamelCase, 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
andandorfor 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 aResultwill either early return theError unwrap theOkvalue, just like Rust- If you put a space between the expression and
?, it will use the function after the?to map theErrvalue first before the early return happens- This is really useful for giving additional useful for giving context
??unwraps aResultwith a default value just likeResult.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 --migrateand 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.mapwithResult.map_ok - Introduce some new
Stroperations:Str.with_ascii_lowercased: make ASCII characters in a string lowercase and leave everything else the sameStr.with_ascii_uppercased: make ASCII characters in a string uppercase and leave everything else the sameStr.caseless_ascii_equals: compare the contents of two strings, ignoring case for ASCII charactersStr.from_utf8: convert aList U16to 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 aList U8to a string, replacing invalid codepoints with the "�" characterStr.from_utf16: convert aList U16to a string, failing on invalid codepointsStr.from_utf16_lossy: convert aList U16to a string, replacing invalid codepoints with the "�" characterStr.from_utf32: convert aList U16to a string, failing on invalid codepointsStr.from_utf32_lossy: convert aList U32to a string, replacing invalid codepoints with the "�" character
- Added some effectful
Listwalking functions:for_each!: Run an effectful operation on every element in a listfor_each_try!: Run an effectful operation on every element in a list, returning early on anErrwalk!: Effectfully walk through the elements of a list, building up state as you gowalk_try!: Effectfully walk through the elements of a list, building up state as you go, returning early on anErr
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
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
🕑 These releases are updated regularly, don't mind the Oct 22, 2021 timestamp. Ask Anton on zulip chat if you need older nightly releases.
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"