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
Result
in 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.