Welcome to the new Golem Cloud Docs! 👋
Rib

Rib

Rib is the language used by the API Gateway that enables users to write programs capable of manipulating worker responses, which are WebAssembly (WASM) values.

Rib uses the WAVE (WebAssembly Value Encoding) (opens in a new tab) syntax for encoding values.

Rib Grammar

rib-expr below defines the grammar of Rib.

rib_expr          ::= rib_expr_untyped (":" type_name)?

rib_expr_untyped  ::= simple_expr rib_expr_rest

simple_expr       ::= simple_expr_untyped (":" type_name)?

simple_expr_untyped ::=
      list_comprehension
    | list_reduce
    | pattern_match
    | let_binding
    | conditional
    | flag
    | record
    | code_block
    | tuple
    | sequence
    | boolean_literal
    | literal
    | not
    | option
    | result
    | identifier
    | call
    | signed_num


rib_expr_rest       ::= binary_rest | method_invocation | select_field_rest | select_index_res | number_rest  |  range_rest

select_field_rest   ::=  ("." select_field)*

binary_rest         ::=  (binary_op simple_expr)*

number_rest         ::=  "." fraction

method_invocation   ::= "." call

call                ::= function_name ("(" argument ("," argument)* ")")?

function_name       ::= variant | enum | worker_function_call

select_index_rest   ::= ("[" select_index "]")*

binary_op           ::=  ">=" | "<=" | "==" | "<" | ">" | "&&" | "||" | "+" | "-" | "*" | "/"

range_rest          ::= ".." ("="? pos_num | pos_num? )

fraction            ::= digits (('e' | 'E') ('+' | '-')? digits)?

let_binding         ::= "let" let_variable (":" type_name)? "=" rib_expr

conditional         ::= "if" rib_expr "then" rib_expr "else" rib_expr

selection_expr      ::= select_field | select_index

select_field        ::= selection_base_expr "." identifier

select_index        ::= pos_num

nested_indices      ::= "[" pos_num "]" ("," "[" pos_num "]")*

flag                ::= "{" flag_names "}"

flag_names          ::= flag_name ("," flag_name)*

flag_name           ::= (letter | "_" | digit | "-")+

record              ::= "{" record_fields "}"

record_fields       ::= field ("," field)*

field               ::= field_key ":" rib_expr

field_key           ::= (letter | "_" | digit | "-")+

code_block          ::= rib_expr (";" rib_expr)*

code_block_unit     ::= code_block ";"

selection_base_expr ::= select_index | select_field | identifier | sequence | tuple

tuple               ::= "("  ib_expr ("," rib_expr)* ")"

sequence            ::= "["  rib_expr ("," rib_expr)* "]"

enum                ::= identifier

variant             ::= identifier ( "(" rib_expr ")" )   ?

argument            ::= rib_expr

list_comprehension  ::= "for" identifier_text "in" expr "{"
                           code_block_unit?
                           "yield" expr ";"
                         "}"

list_reduce         ::= "reduce" identifier_text"," identifier_text "in" expr "from" expr "{"
                       code_block_unit?
                       "yield" expr ";"
                     "}"

text                ::= letter (letter | digit | "_" | "-")*

pos_num             ::= digit+

digits              ::= [0-9]+

signed_num          ::= ("+" | "-")? pos_num

literal             ::= "\"" (static_term | dynamic_term)* "\""

static_term         ::= (letter | space | digit | "_" | "-" | "." | "/" | ":" | "@")+

dynamic_term        ::= "${" rib_expr "}"

boolean_literal     ::= "true" | "false"

not                 ::= "!" rib_expr

option              ::= "some" "(" rib_expr ")" | "none"

result              ::= "ok" "(" rib_expr ")" | "error" "(" rib_expr ")"

identifier          ::= any_text

function_name       ::= variant | enum | instance | text

instance            ::= "instance"

any_text            ::= letter (letter | digit | "_" | "-")*

type_name           := basic_type | list_type | tuple_type | option_type | result_type

basic_type          ::= "bool" | "s8" | "u8" | "s16" | "u16" | "s32" | "u32" | "s64" | "u64" | "f32" | "f64" | "char" | "string"

list_type           ::= "list" "<" (basic_type | list_type | tuple_type | option_type | result_type) ">"

tuple_type          ::= "tuple" "<" (basic_type | list_type | tuple_type | option_type | result_type) ("," (basic_type | list_type | tuple_type | option_type | result_type))* ">"

option_type         ::= "option" "<" (basic_type | list_type | tuple_type | option_type | result_type) ">"

result_type         ::= "result" "<" (basic_type | list_type | tuple_type | option_type | result_type) "," (basic_type | list_type | tuple_type | option_type | result_type) ">"

The grammar is mainly to give a high level overview of the syntax of Rib.

The following sections show some examples of each construct.

Rib scripts can be multiline, and it should be separated by ;.

Return type of a Rib script

The last expression in the Rib script is the return value of the script. The last line in Rib script shouldn't end with a ;, as this is a syntax error.

Let's see how we can write Rib corresponding to the types supported by the WebAssembly Component Model (opens in a new tab)

Core Language Elements

Identifier

foo

Here foo is an identifier. Note that identifiers are not wrapped with quotes.

Rib will fail at compilation if variable foo is not available in the context.

This implies, if you are running Rib specifying a wasm component through api-gateway in golem, then Rib has the access to look at component metadata. If Rib finds this variable foo in the component metadata,then it tries to infer its types.

If such a variable is not present in the wasm component (i.e, you can cross check this in WIT file of the component), then technically there are two possibilities. Rib will choose to fail the compilation or consider it as a global variable input. A global input is nothing but Rib expects foo to be passed in to the evaluator of Rib script (somehow).

Since you are using Rib from the api-gateway part of golem the only valid global variable supported is request and nothing else. This would mean, it will fail compilation.

Example:

 
my-worker-function(request)
 

request here is a global input and Rib knows the type of request if my-worker-function is a valid function in the component (which acts as dependencies to Rib)

Here are a few other examples of identifiers:

foo-bar
FOO-BAR

More on global inputs to follow.

Primitives

https://component-model.bytecodealliance.org/design/wit.html#primitive-types (opens in a new tab)

Numbers

1

You can annotate a type to Rib expression to make it specific, otherwise, it will get inferred as u64 for a positive integer, s64 for a signed integer, and f64 for a floating point number.

1: u64

Boolean

true
false

String

"foo"

Lists

# Sequence of numbers
[1, 2, 3]

You can annotate the type of the list as follows:

let x: list<s32> = [1, 2, 3];
["foo", "bar", "baz"]
# Sequence of record
[{a: "foo"}, {b : "bar"}]

Options

Option in corresponding to WASM , which can take the shape of Some or None. This is similar to Option type in std Rust.

An Option can be Some of a value, or None.

 some(1)
 none

You can annotate the type of option as follows, if needed.

let x: option<u32> = none;

The syntax is inspired from wasm wave.

Results

Resultin Rib is WASM Result, which can take the shape of ok or err. This is similar to Result type in std Rust.

A Result can be Ok of a value, or an Err of a value.

 ok(1)
 err("error")
 
{
  "user": "ok(Alice)",
  "age": 30u32
}

Note that there are various possibilities (opens in a new tab) of result in Web Assembly component model.

If there is no data associated with the error case, you can do the following:

let my-result: result<_, string> = err("foo")
my-result

If there are no data associated with the success case, you can do the following:

let my-result: result<string> = ok("foo")
 

If you don't care the type of success and error.

let my-result: result = ok("foo")

When using rib, we recommend using the mostly used pattern which is to know both the success and error case (i.e, result<u32, string>)

Tuples

A tuple type is an ordered fixed length sequence of values of specified types. It is similar to a record, except that the fields are identified by their order instead of by names.

(1, 20.1, "foo")

This is also equivalent to the following in Rib. You can be specific about the types just like in any rib expression.

let my-tuple: tuple<u64, f64, string> = (1, 20.1, "foo");
my-tuple

Unlike list, the values in a tuple can be of different types.

Here is another example:

("foo", 1, {a: "bar"})

Record

{ name: "John", age: 30 }
 
{ city: "New York", population: 8000000 }
 
{ name: "John", age: 30, { country: "France", capital: "Paris" } }

This is parsed as a WASM Record. The syntax is inspired from WASM-WAVE.

Note that, sometimes you will need to annotate the type of the number. It depends on the compilation context.

Note that keys are not considered as variables. Also note that keys in a WASM record don't have quotes. Example: {"foo" : "bar"} is wrong.

Variants

Let say your WIT file has the following variant (opens in a new tab)


variant bid-result {
   success,
   failure(string),
}

bid: func() -> bid-result;

process-result: func(res: bid-result) -> string

Then, in Rib, all you need to is simply use the variant terms directly. Example:

let x = failure("its failed");
x

You can pattern match (more on this patter-match below) on variants as follows:

let x = failure("its failed");
 
let result = match x { success => "its success", failure(msg) => msg }
 
result

This will return "its failed".

Here is the example WIT file and the corresponding rib scripts that shows how to pass variants as arguments to worker function. More explanation on how to invoke worker function using rib is explained further down below.

  variant bid-result {
    success(string),
    failure(string)
  }

  handle: func(res: bid-result) -> string;
let worker = instance("my_worker");
worker.handle(failure("it's failed"));
let worker = instance("my_worker");
worker.handle(success);
let worker = instance("my_worker");
let bid-result = worker.bid();
let result = worker.handle(bid-result);
result
 

Note that rib script should have the dependency to the component that defines the variant. Otherwise, Rib will fail with compilation.

failure("its failed")
[invalid script] error in the following rib found at line 1, column 203
`failure("its failed")`
cause: invalid function call `failure`
unknown function

If Rib is used through API gateway and not directly, this dependency is already added automatically as you specify component name when defining API definition.

Enums

Let's say your WIT file has the following enum (opens in a new tab) types,

  enum status {
    backlog,
    in-progress,
    done,
  }

  enum priority {
    low,
    medium,
    high,
  }

Then, in Rib, all you need to do is to simply specify the name of the term. Example by typing in backlog, it knows its an enum type which can be any of backlog, in-progress and done.

let my-status = backlog;
let my-priory = priority;
{s: my-status, p: my-priority}

You can pattern match (more on this pattern-match below) on enum values as follows

 
let my-priority = low;
 
let result = match my-priority {
  low => "its low",
  medium => "its medium",
  high => "its high"
};
 
result

This will return the result "its low".

Here is the example WIT file and the corresponding rib scripts that shows how to pass variants as arguments to worker function. More explanation on how to invoke worker function using rib is explained further down below.

  enum status {
    backlog,
    in-progress,
    done,
  }

  enum priority {
    low,
    medium,
    high,
  }

  process-user: func(status: status, priority: priority, user-id: string) -> string;

 
let worker = instance("my_worker");
let result = worker.process-user(backlog, low, "jon");
result
 
let worker = instance("my_worker");
let status = in-progress;
let priority = medium;
let result = worker.process-user(status, priority, "jon");
result

Note that rib script should have the dependency to the component that defines the enum. Otherwise, Rib will fail with compilation.

If Rib is used through API gateway and not directly, this dependency is already added automatically as you specify component name when defining API definition.

Flags

{ Foo, Bar, Baz }

This is of a flag type.

Expressions and Syntax Features

Assign to a variable

This can be done using let binding which we have already seen in the above examples

let x = 1;

We can annotate the type of the variable as well.

let x: u64 = 1;

If you are passing this variable to a worker function, then you may not need to specify the type of the variable.

String Interpolation (concatenation)

This is similar to languages like scala where you start with ${ and end with }

let x = "foo";
let y = "bar";
let z = "${x}-and-${y}";

Evaluating this Rib will result in "foo-and-bar". The type of z is a string.

Selection of Field

A field can be selected from an Rib expression if it is a Record type. For example, foo.user is a valid selection given foo is a variable that gets evaluated to a record value.

let foo = { name: "John", age: 30 };
foo.name

Selection of Index

This is selecting an index from a sequence value.

let x = [1, 2, 3];
x[0]

You can also inline as given below

[1, 2, 3][0]

Comparison (Boolean)

let x: u8 = 1;
let y: u8 = 2;
x == y
 

Similarly, we can use other comparison operators like >=, <=, ==, < etc. Both operands should be a valid Rib code that points/evaluated to a number or string.

Arithmetic

let x: u8 = 1;
let y: u8 = 2;
x + y

+, -, / and * are supported.

Collections and Control Structures

List Comprehension

 let x = ["foo", "bar"];
 
 for p in x {
   yield p;
 }

List Aggregation

 let ages: list<u16> = [1, 2, 3];
 
 reduce z, a in ages from 0 {
   yield z + a;
 }

Ranges

Ranges can be right exclusive or right inclusive. The right exclusive range is denoted by .. and right inclusive range is denoted by ..=.

1..10;
let initial: u32 = 1;
let final: u32 = 10;
 
let x = initial..final;
 
for i in x {
  yield i
}

Similarly, you can use ..= to include the last number in the range.

1..=10;
let initial: u32 = 1;
let final: u32 = 10;
 
let x = initial..=final;
 
for i in x {
  yield i
}

Please note that, you may need to help Rib compiler with type annotations for the numbers involved in the range. This depends on the context. We will improve these aspects as we go.

You can also create infinite range, where you skip the right side of ..

1..;

However, note that as of now Rib interpreter (runtime) will spot any infinite loop and will throw an error. Example: The following will throw an error.

let x = 1:u8..;
 
for i in x {
  yield i
}

However, you can use infinite ranges to select a segment of the list without worrying about the end index.

let x: list<u32> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
for i in x[1..] {
  yield i
}

Pattern Matching

let res: result<str, str> = ok("foo");
 
match res {
    ok(v) => v,
    err(msg) => msg
}

This would probably be your go-to construct when you are dealing with complex data structures like result or option or other custom variant (WASM Variant) that comes out as the response of a worker function

  let worker_result = my_worker_function_name(1, "jon");
  match worker_result {
     ok(x) => "foo",
     err(msg) => "error"
  }

Exhaustive pattern matching

If the pattern match is not exhaustive, then it will throw compilation errors

Example: The following pattern matching is invalid

  match worker_result {
    some(x) => "found",
  }

This will result in following error:

Error: Non-exhaustive pattern match. The following patterns are not covered: `none`.
To ensure a complete match, add these patterns or cover them with a wildcard (`_`) or an identifier.

Now, let's say your worker responded with a variant. Note on variant: A variant statement defines a new type where instances of the type match exactly one of the variants listed for the type. This is similar to a "sum" type in algebraic datatype (or an enum in Rust if you're familiar with it). Variants can be thought of as tagged unions as well.

Pattern Match on Variants

Given you are using Rib through worker bridge, which knows about component metadata, then, let's say we have a variant of type as given below responded from the worker, when invoking a function called foo:

variant my_variant {
    bar( {a: u32,b: string }),
    foo(list<string>),
}

then, the following will work:

let x = foo("abc");
 
match x {
  bar({a: _, b: _}) => "success",
  foo(some_list) => some_list[0]
}
 

Variables in the context of pattern matching

In all of the above, there exist a variable in the context of pattern. Example x in ok(x) or msg in err(msg) or x in some(x) or x in bar(x) or x in foo(x).

These variables are bound to the value that is being matched.

Example, given the worker response is ok(1), the following match expression will result in 2.

   let result = my-worker-function("foo");
 
   match result {
    ok(x) => x + 1,
    err(msg) => 0
  }

The following match expression will result in "c", if the worker response was variant value foo(["a", "b", "c"]), and will result in "a" if the worker.response was variant value bar({a: 1, b: "a"}).

  let result = my-worker-function();
 
  match result {
    bar(x) => x.b,
    foo(x) => x[1]
  }
 

Wild card patterns

In some of the above examples, the binding variables are unused. We can use _ as a wildcard pattern to indicate that we don't care about the value.

  match worker.response {
    bar(_) => "bar",
    foo(_) => "foo"
  }

Conditional Statement

 let id: u32 = 10;
 if id > 3 then "higher" else "lower"

The structure of the conditional statement is if <condition> then <then-rib-expr> else <else-rib-expr>, where condition-expr is an expr that should get evaluated to boolean. The then-rib-expr or else-rib-expr can be an any valid rib code, which could be another if else itself

 let id: u32 = request.user.id;
 if id > 3 then "higher" else if id == 3 then "equal" else "lower"

You must ensure that the branches (then branch and else branch) resolve to the same type. Otherwise, Rib will fail to compile.

External Interactions

Http Request Input and Field Selection

Rib is currently mainly used in worker-gateway which will allow us to expose Http APIs on top of worker functions. If you are using Rib in the context of worker-gateway then you can use the variable request in your Rib script, which allows you to access various parameters of input Http request. In other words, request is a global input to the Rib script when used in API definitions, and worker-gateway will ensure to pass the value of request to the rib evaluator internally.

Please refer to worker-gateway documents for more details.

request will hold all the details of an input Http request in gateway, such as headers, path, query parameters and body.

Request Body Parameters

To select the body field in request,

request.body.user

Request Header Parameters

To select a header from the request

request.headers.user

Request Path Parameters

To select the value of a path variable in your http request /v4/{user-id}/get-cart-contents

request.path.user-id

Request Query Parameters

To select the value of a query variable in your http request /v4/{user-id}/get-cart-contents?{country}

request.query.country

The request.path.* and request.headers.* will be inferred as string unless you specify it using type annotation.

Example:

let user-id: u8 = request.path.user-id;

To select the value of a query variable in your http request /v4/{user-id}/get-cart-contents?{country}

let country: string = request.query.country;

A typical Rib script in API definition (worker gateway)

Here is a full example of a Rib script that look up various elements in a Http Request.

Given the route is /v4/{user-id}/get-cart-contents?{country} , which is a POST request, which has a body, then the following Rib script can be valid, and has a dependency to a component that has a function my-function that takes two arguments of type {foo: string, bar: string} and string

let user-id: u8 = request.path.user-id;
let country: string = request.query.country;
let input = request.body;
let worker = instance("my-worker-${user-id}");
let result = worker.my-function(input, country);
result

This Rib script will be part of an API definition (please refer to worker-gateway for more details) When registering this API definition, it already keeps track of the requirements of the input http request. In this case, the input request is a POST request with a body, and the body is of type {foo: string, bar: string}, and the path variable is user-id of type u8, and the query parameter is country of type string.

Once the API is deployed and the API is invoked, worker gateway will validate the input request against these requirements, and if they don't satisfy, it results in a BadRequest error.

Please note the language Rib by itself don't support a keyword such as request, or path or body or headers. The above examples are valid only if are using Rib through worker-gateway. If Rib script is evaluated in the context of a http request, these values are available in the context of the Rib evaluator.

Currently, there are two ways to use Rib in golem. One is worker-gateway and the other is golem REPL. In the case of REPL, request is not available, since we are not executing Rib in the context of an Http request.

Here is an example of an invalid Rib script

 
request
 
request.body

In the above case, compiler is not able to infer the type of request or request.body as it is not passed as an argument to a worker function.

Invoke worker functions

Rib is mainly used to write scripts to manipulate the input and call worker functions. Refer to the worker-gateway documentation for more on how you use Rib to invoke worker functions. This is useful to expose some http APIs on top of these worker functions running in golem platform, and you don't want to keep updating or change the components and expose additional APIs, as you can write a simple Rib script to do the necessary changes to the input and pass it to the worker functions. Invoking functions is similar to any other languages.

Durable worker function invocation

let my_worker = instance("my-worker");
let result = my_worker.get-cart-contents();
 
result

Rib is evaluated in the context of a particular component (this is taken care by worker-gateway that it evaluates Rib in the context of a wasm component).

In this script, first you create an instance (instance of a component) using instance function. instance function takes worker name as the argument. Once you created the instance, the functions in the component will be available to call.

Ephemeral Worker function invocation

The only difference here is that you don't need to pass an argument to the instance function.

let my_worker_instance = instance();
let result = my_worker.get-cart-contents();
result

You can avoid an unnecessary let-binding here too.

let my_worker_instance = instance();
my_worker.get-cart-contents();

In this case the return value of the script is the last expression in the script, and in this case, it is of the result type of get-cart-contents.

Worker function invocation with arguments

Let's say there exists a function add-to-cart which takes a product as the argument, where product is a wasm record

let my_worker_instance = instance("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
my_worker.add-to-cart(input);

Similarly you can pass multiple arguments to the worker function and they should be separated by ,.

Say you need to pass the username along with with the product details.

let my_worker_instance = instance("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
let username = "foo";
 
my_worker.add-to-cart(username, product);

You can inline the arguments as well.

let my_worker_instance = instance("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
my_worker.add-to-cart("foo", {product-id: 1, quantity: 2, name: "mac"});

Invoke functions in a Resource

Here is a relatively complex example where Rib is used to invoke functions in a resource cart Here the first step is to define the worker by calling instance function. Then you create a resource similar to a method invocation which is worker.cart. Here the only difference is cart is a resource rather than a function. Now you have the resource available to call methods on it such as add-item, remove-item etc.

Please note that, everything prior to a real function call is lazy. i.e, you are not reusing the same resource at runtime to call these functions.

  let worker = instance("my-worker");
  let cart = worker.cart("bar");
  cart.add-item({product-id: "mac", name: "apple", quantity: 1, price: 1});
  cart.remove-item(a);
  cart.update-item-quantity(a, 2);
  let result = cart.get-cart-contents();
  cart.drop();
  result

Handle conflicting function names using type parameters

Let's say a function name add-to-cart exists in multiple interfaces (say api1, api2) in the same component. In this case, you can specify the interface as a type parameter when invoking method in the instance.

Example:

let my_worker_instance = instance("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
 
let result = my_worker.add-to-cart[api1](product);
 
result

If you are not specifying type parameter that narrows down the context, then compiler will return an error similar to the below one:

error in the following rib found at line 3, column 30
`my_worker.add-to-cart(product)`
cause: invalid function call `qux`
function 'add-to-cart' exists in multiple interfaces. specify an interface name as type parameter from: api1, api2

Handle conflicting packages using type parameters

Let's say a function name add-to-cart exists in multiple packages. In this case, you can specify the package name too as a type parameter. Let's say you care only about the package amazon:shopping-cart.

let my_worker_instance = instance("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
 
let result = my_worker.add-to-cart[amazon:shopping-cart](product);
 
result

Handle conflicts at instance level

You can also include this type parameter at instance level such that every method invocation will be resolved to that package or interface.

let my_worker_instance = instance[amazon:shopping-cart]("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
 
let result = my_worker.add-to-cart(product);

Conflict resolution using fully qualified type parameter

If there exist a function add-to-cart in multiple interfaces within multiple packages, then you can specify fully qualified package name and interface as given below. As mentioned above, this can be either at instance level (which will get applied to all method calls on that instance) or at method level

let my_worker_instance = instance[amazon:shopping-cart/api]("my-worker");
 
let product = {product-id: 1, quantity: 2, name: "mac"};
 
let result = my_worker.add-to-cart(product);

Multiple invocations

Rib allows you to invoke a function multiple times, or invoke multiple functions across multiple workers in a single script. That said, it is important to note that Rib by itself is not durable and is stateless. The next time you invoke Rib (through worker-gateway for example), these functions will get executed against the worker again and doesn't cache any result in anyway.

Let's say we want to accumulate the contents of a shopping cart from user-id 1 to user-id 5.

  let worker = instance("my-worker");
  let cart = worker.cart[golem:it/api]("bar");
 
  let initial = 1: u64;
  let final = 5: u64;
  let range = initial..final;
 
  let result = for i in range {
     yield cart.get-cart-contents("user-id-${i}");
  };
 
  result
 

Currently Rib is not durable. Also it hasn't been tested with complex use-cases such as invoking multiple functions, or invoke functions against multiple workers. This is because, Rib's primary use-case in golem platform is for users to write reasonably simple scripts in the context of worker-gateway to manipulate the http input and call worker functions.

Type Inference

Rib is mainly used to write script to manipulate the input and call worker functions. In this case, for the most part, most of the types will be automatically inferred. Otherwise, there will be a compilation error, asking the developer to annotate the types and help the compiler.

let x: string = request.body;
x

Say the request body is a record in Json, as given below. Rib sees it as a WASM Record type.

{
  "user": "Alice",
  "age": 30u32
}

Then we can use Rib language to select the field user, as it considers this request's body as a WASM Record type.

let x: string = request.body.user;
x

Limitations

We recommend the users of golem to not rely on Rib for complex business logic as of now. It's better to write it in standard languages that works with golem such as Rust, Python etc, making sure your logic or workflow is durable. We will be expanding the usability and reliability of Rib as we go.

Issues and Trouble Shooting

If you bump into compilation errors, annotating the types will help the compiler to infer the types properly. If you bump into internal runtime errors, please report to us and we will try to fix it as soon as possible.

We are making improvements in this space continuously.