All Syntax File
Demonstrates all Roc syntax in a single app file. See all module types to view syntax examples for the non-app headers.
Code
app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" } import cli.Stdout import cli.Stdout as StdoutAlias import cli.Arg exposing [Arg] import "README.md" as readme : Str # You can also import as List U8 # Note 1: I tried to demonstrate all Roc syntax (possible in a single app file), # but I probably forgot some things. # Note 2: Lots of syntax patterns are better explained in their own dedicated example, # see https://www.roc-lang.org/examples/ ## Double hashtag for doc comment number_operators : I64, I64 -> _ number_operators = |a, b| a_f64 = Num.to_f64(a) b_f64 = Num.to_f64(b) { # binary operators sum: a + b, diff: a - b, prod: a * b, div: a_f64 / b_f64, div_trunc: a // b, rem: a % b, eq: a == b, neq: a != b, lt: a < b, lteq: a <= b, gt: a > b, gteq: a >= b, # unary operators neg: -a, # the last item can have a comma too } boolean_operators : Bool, Bool -> _ boolean_operators = |a, b| { bool_and: a && b, bool_and_keyword: a and b, bool_or: a || b, bool_or_keyword: a or b, not_a: !a, } pizza_operator : Str, Str -> Str pizza_operator = |str_a, str_b| str_a |> Str.concat(str_b) patterns : List U64 -> U64 patterns = |lst| when lst is [1, 2, ..] -> 42 [2, .., 1] -> 24 [] -> 0 [_head, .. as tail] if List.len(tail) > 7 -> List.len(tail) # Note: avoid using `_` in a when branch, in general you should # try to match all cases explicitly. _ -> 100 string_stuff : Str string_stuff = planet = "Venus" Str.concat( "Hello, ${planet}!", """ This is a multiline string. You can call functions inside $... too: ${Num.to_str(1 + 1)} Unicode escape sequence: \u(00A0) """, ) pattern_match_tag_union : Result {} [StdoutErr(Str), Other] -> Str pattern_match_tag_union = |result| # `Result a b` is the tag union `[Ok a, Err b]` under the hood. when result is Ok(_) -> "Success" Err(StdoutErr(err)) -> "StdoutErr: ${Inspect.to_str(err)}" Err(_) -> "Unknown error" # end name with `!` for effectful functions # `=>` shows effectfulness in the type signature effect_demo! : Str => Result {} [StdoutErr _, StdoutLineFailed [StdoutErr _]] effect_demo! = |msg| # `?` to return the error if there is one Stdout.line!(msg)? # ` ? ` for map_err Stdout.line!(msg) ? |err| StdoutLineFailed(err) # this also works: Stdout.line!(msg) ? StdoutLineFailed # ?? to provide default value Stdout.line!(msg) ?? {} # In rare cases, you can use `_ =` to ignore the result. # This allows you to avoid StdoutErr in the type signature. # Example of appropriate usage: # https://github.com/roc-lang/basic-webserver/blob/main/platform/main.roc _ = Stdout.line!(msg) Ok({}) dbg_expect : {} -> {} dbg_expect = |{}| a = 42 dbg a # dbg can forward what it receives b = dbg 43 # inline expects get removed in optimized builds! expect b == 43 {} # Top level expect expect 0 == 0 # Values that are defined inside a multi-line expect get printed on failure expect expected = 43 actual = 44 actual == expected if_demo : U64 -> Str if_demo = |num| # every if must have an else branch! one_line_if = if num == 1 then "True" else "False" # multiline if if num == 2 then one_line_if else if num == 3 then "False" else "False" tuple_demo : {} -> (Str, U32) tuple_demo = |{}| # tuples can contain mutltiple types # they are allocated on the stack ("Roc", 1) tag_union_demo : Str -> [Red, Green, Yellow] tag_union_demo = |string| when string is "red" -> Red "green" -> Green # We can't list all possible strings, so we use `_` to match all other cases. _ -> Yellow type_var_star : List * -> List _ type_var_star = |lst| lst TypeWithTypeVar a : [ TagOne, TagTwo Str, ]a tag_union_advanced : Str -> TypeWithTypeVar [TagThree, TagFour U64] tag_union_advanced = |string| when string is "one" -> TagOne "two" -> TagTwo("hello") "three" -> TagThree # We can't list all possible strings, so we use `_` to match all other cases. _ -> TagFour(42) default_val_record : { a ?? Str } -> Str default_val_record = |{ a ?? "default" }| a destructuring = tup = ("Roc", 1) (str, num) = tup rec = { x: 1, y: tup.1 } # tuple access with `.index` { x, y } = rec (str, num, x, y) record_update = rec = { x: 1, y: 2 } rec2 = { rec & y: 3 } rec2 record_access_func = .x # You can pass a record with many more fields than just x and y. open_record_arg_sum : { x: U64, y: U64 }* -> U64 open_record_arg_sum = |{ x, y }| x + y number_literals = usage_based = 5 explicit_u8 = 5u8 explicit_i8 = 5i8 explicit_u16 = 5u16 explicit_i16 = 5i16 explicit_u32 = 5u32 explicit_i32 = 5i32 explicit_u64 = 5u64 explicit_i64 = 5i64 explicit_u128 = 5u128 explicit_i128 = 5i128 explicit_f32 = 5.0f32 explicit_f64 = 5.0f64 explicit_dec = 5.0dec hex = 0x5 octal = 0o5 binary = 0b0101 (usage_based, explicit_u8, explicit_i8, explicit_u16, explicit_i16, explicit_u32, explicit_i32, explicit_u64, explicit_i64, explicit_u128, explicit_i128, explicit_f32, explicit_f64, explicit_dec, hex, octal, binary) # Using `where` ... `implements` to_str : a -> Str where a implements Inspect to_str = |value| Inspect.to_str(value) # Opaque type Username := Str username_from_str : Str -> Username username_from_str = |str| @Username(str) username_to_str : Username -> Str username_to_str = |@Username(str)| str # Opaque type with derived abilities StatsDB := Dict Str { score : Dec, average : Dec } implements [ Eq, Hash ] # Custom implementation of an ability Animal := [ Dog Str, Cat Str, ] implements [ Eq { is_eq: animal_equality }, ] animal_equality : Animal, Animal -> Bool animal_equality = |@Animal(a), @Animal(b)| when (a, b) is (Dog(name_a), Dog(name_b)) | (Cat(name_a), Cat(name_b)) -> name_a == name_b _ -> Bool.false # Defining a new ability CustomInspect implements inspect_me : val -> Str where val implements CustomInspect Color := [Red, Green] implements [ Eq, CustomInspect { inspect_me: inspect_color, }, ] inspect_color : Color -> Str inspect_color = \@Color color -> when color is Red -> "Red" Green -> "Green" early_return = |arg| first = if !arg then return 99 else "continue" # Do some other stuff Str.count_utf8_bytes(first) record_builder_example = parser = { chain <- name: parse(Ok), age: parse(Str.to_u32), city: parse(Ok), } |> run parser("Alice-25-NYC") # record builder helpers Builder a := List Str -> Result (a, List Str) [Empty] parse : (Str -> Result a [Empty]) -> Builder a parse = |f| @Builder |segments| when segments is [] -> Err(Empty) [first, .. as rest] -> when f(first) is Ok(value) -> Ok((value, rest)) Err(_) -> Err(Empty) chain : Builder a, Builder b, (a, b -> c) -> Builder c chain = |@Builder(fa), @Builder(fb), combine| @Builder |segments| (a, rest1) = fa(segments)? (b, rest2) = fb(rest1)? Ok((combine(a, b), rest2)) run : Builder a -> (Str -> Result a [Empty]) run = |@Builder(f)| |input| segments = Str.split_on(input, "-") (result, _) = f(segments)? Ok(result) # end record builder helpers main! : List Arg => Result {} _ main! = |_args| Stdout.line!("${Inspect.to_str(number_operators(10, 5))}")? Stdout.line!("${Inspect.to_str(boolean_operators(Bool.true, Bool.false))}")? pizza_out = pizza_operator("Pizza ", "Roc") Stdout.line!("${Inspect.to_str(pizza_out)}")? StdoutAlias.line!("${Inspect.to_str(patterns([1, 2, 3, 4]))}")? Stdout.line!("${string_stuff}")? Stdout.line!("${Inspect.to_str(pattern_match_tag_union(Ok({})))}")? Stdout.line!("${Inspect.to_str(effect_demo!("Hello, world!"))}")? Stdout.line!("${Inspect.to_str(if_demo(1))}")? Stdout.line!("${Inspect.to_str(tuple_demo({}))}")? Stdout.line!("${Inspect.to_str(tag_union_demo("red"))}")? Stdout.line!("${Inspect.to_str(type_var_star([1, 2]))}")? Stdout.line!("${Inspect.to_str(tag_union_advanced("four"))}")? Stdout.line!("${default_val_record({})}")? Stdout.line!("${Inspect.to_str(destructuring)}")? Stdout.line!("${Inspect.to_str(record_update)}")? Stdout.line!("${Inspect.to_str(record_access_func({ x: 44, y: 3 }))}")? Stdout.line!("${Inspect.to_str(dbg_expect({}))}")? Stdout.line!("${Num.to_str(open_record_arg_sum({ x: 1, y: 3 }))}")? Stdout.line!("${Inspect.to_str(number_literals)}")? Stdout.line!("${username_to_str(username_from_str("Rocco"))}")? Stdout.line!("${to_str(42)}")? Stdout.line!("${Inspect.to_str(early_return(Bool.false))}")? Stdout.line!("${Inspect.to_str(record_builder_example)}")? Stdout.line!("${Inspect.to_str(Str.count_utf8_bytes(readme) > 0)}")? # Commented out so CI tests can pass # crash "Avoid using crash in production software!" Ok({})
Output
Run this from the directory that has main.roc
in it:
$ roc main.roc {diff: 5, div: 2, div_trunc: 2, eq: Bool.false, gt: Bool.true, gteq: Bool.true, lt: Bool.false, lteq: Bool.false, neg: -10, neq: Bool.true, prod: 50, rem: 0, sum: 15} {bool_and: Bool.false, bool_and_keyword: Bool.false, bool_or: Bool.true, bool_or_keyword: Bool.true, not_a: Bool.false} "Pizza Roc" 42 Hello, Venus!This is a multiline string. You can call functions inside $... too: 2 Unicode escape sequence: "Success" Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! (Ok {}) "False" ("Roc", 1) Red [1, 2] (TagFour 42) default ("Roc", 1, 1, 1) {x: 1, y: 3} 44 [./examples/AllSyntax/main.roc:119] a = 42 [./examples/AllSyntax/main.roc:122] 43 = 43 {} 4 (5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5.0, 5, 5, 5) Rocco 42 99 (Ok {age: 25, city: "NYC", name: "Alice"}) Bool.true