Data Types#

Data Types#

Data type name

Ride keyword

Big Integer

BigInt

Boolean

Boolean

Byte array

ByteVector

Integer

Int

String

String

Empty value

Unit

List

List

Tuple

Union

Structure

Arbitrary type

Any

For each value, depending on the data type, the weight is determined. The weight is used in limitations on creating and comparing values. For more information see the data weight.

Any#

Any is an arbitrary data type. It is a common supertype of all types: an Any type value can be a string, a number, unit, a structure, a list, a tuple, etc.

func findString(a: Any) = {
 match a {
   case a: String => a
   case a: List[Any] =>
     match a[0] {
       case b: String => b
       case _ => throw("Data is not a string")
     }
   case _ => throw("Data is not a string")
 }
}

BigInt#

BigInt is a special numeric data type designed to handle values outside the range of Int and to perform high accuracy calculations. BigInt variable has a size of \(64\) bytes (\(512\) bits) and contains an integer between \(–2511\) to \(2511–1\), inclusive. The weight of the value is \(64\). A BigInt variable can only be used inside a script. A callable function does not accept arguments of BigInt type and does not return a value of BigInt type. You can pass a big integer value as a string, then use the parseBigInt or parseBigIntValue functions.

BigInt Operations#

The following operators support BigInt values:

  • Arithmetic operators: +, -, *, /, %, unary minus.

  • Comparison operators: <, >, <=, and >=.

  • Equality operators: == and !=.

BigInt Functions#

The following functions operate BigInt values:

Boolean#

Boolean is a data type that can have only the values true or false.

ByteVector#

ByteVector is a data type for byte array.

To assign a value to a ByteVector variable, you can use a string in Base16, Base58, or Base64 with the appropriate prefix:

let a = base16'52696465'
let b = base58'8t38fWQhrYJsqxXtPpiRCEk1g5RJdq9bG5Rkr2N7mDFC'
let c = base64'UmlkZQ=='

This method, unlike the fromBase16String, fromBase58String, and fromBase64String functions, does not increase the complexity of the script, since decoding is performed by the compiler. To convert integer, boolean and string values to a byte array use toBytes function:

let a = 42.toBytes()
let b = true.toBytes()
let c = "Ride".toBytes()

For more byte array functions, see the Built-in Functions.

ByteVector Limitations#

The maximum size of a ByteVector variable is \(32,767\) bytes. Exception: the bodyBytes field of transaction structure. You can pass this value as an argument to the rsaVerify и sigVerify verification functions (but cannot concatenate with other byte arrays in case the limit is exceeded).

Int#

Int is an integer data type. The integer variable has the size of 8 bytes and stores an integer from \(-9,223,372,036,854,775,808\) to \(9,223,372,036,854,775,807\) inclusive.

let age = 42
let length = size("hello")

String#

Strings are denoted only using double quotes. They are immutable, and for that reason, the substring function is very efficient: no copying is performed and no extra allocations are required. Strings are UTF-8 encoded.

let name = "Bob"   # use "double" quotes only

String Limitations#

The maximum size of a String variable is \(32,767\) (\(1\) character can take up to \(4\) bytes).

String Functions#

The built-in functions for working with strings are presented in the following articles:

  • String Functions

  • Converting Functions

Unit#

Unit is an empty value data type. The empty value data type is similar to unit in Scala or to null in C#. Usually, built-in functions return unit value of type unit instead of null.

"String".indexOf("substring") == unit # true

Nothing#

Nothing is the ‘bottom type’ of Ride’s type system. No value can be of type nothing, but an expression of type nothing can be used everywhere. In functional languages, this is essential for support for throwing an exception:

2 + throw() # the expression compiles because
   # there's a defined function +(Int, Int).
     # The type of the second operand is Nothing,
     # which complies to any required type

List#

The list data type may contain elements of various types, including nested lists. The maximum number of list items is \(1000\). The nesting depth is not limited. A list doesn’t have any fields, but there are functions and operators in the Standard library that make it easier to work with fields.

  • To prepend an element to an existing list, use the cons function or :: operator

  • To append an element, use the :+ operator

  • To concatenate \(2\) lists, use the ++ operator

let list = [16, 10, 1997, "birthday"]
let last = list[(list.size() - 1)] # "birthday", postfix call of size() function

let initList = [16, 10]                   # init value
let newList = cons(1997, initList)        # [1997, 16, 10]
let newList2 = 1997 :: initList           # [1997, 16, 10]
let newList2 = initList :+ 1              # [16, 10, 1]
let newList2 = [4, 8, 15, 16] ++ [23, 42]     # [4 8 15 16 23 42]

List Operations#

Lists support concatenation as well as adding items to the beginning and the end.

List Operations#

Operation

Symbol

Complexity

Concatenation

++

\(4\)

Adding the element to the end of the list (the list is on the left, the element is on the right)

:+

\(1\)

Adding the element to the beginning of the list (the element is on the left, the list is on the right)

\(2\)

Operation to be used:

nil :+ 1 :+ 2 :+ 3

Result: [1, 2, 3]

Operation to be used:

1 :: 2 :: 3 :: nil

Result: [1, 2, 3]

Operation to be used:

let intList  = [1, 2]             # List[Int]
let strList  = ["3", "4"]         # List[String]
let joined   = intList ++ strList # List[Int|String]
joined

Result: [1, 2, “3”, “4”]

Operation to be used:

let appended = joined :+ true     # List[Boolean|Int|String]
appended

Result: [1, 2, “3”, “4”, true]

Operation to be used:

let nested = intList :: joined  # List[Int|List[Int]|String]
nested

Result: [[1, 2], 1, 2, “3”, “4”]

List Functions#

The built-in list functions are presented in the list functions article. Operations on a list can be implemented via the FOLD macro. The size of the list must be known in advance.

List as Function Argument#

A list, including nested one, can be a function argument:

func foo(arg: List[String|Unit]) = {
...
}

foo(["Ride","DecentralCoins",unit])
func bar(arg: List[List[Int]]) = {
...
}

bar([[1],[],[5,7]])

A callable function can take a list as an argument, but nested lists are not allowed. Here’s an example:

@Callable(i)
func join(strings: List[String|Int]) = {
 let a = match strings[0] {
   case n:Int => toString(n)
   case s:String => s
 }
 let b = match strings[1] {
   case n:Int => toString(n)
   case s:String => s
 }
 let c = match strings[2] {
   case n:Int => toString(n)
   case t:String => t
 }
 [
   StringEntry(toBase58String(i.caller.bytes), a + "_" + b + "_" + c)
 ]
}

Invoke Script transaction example:

{
 "type": 16,
 ...
 "call": {
   "function": "join",
   "args": [
    {
     "type": "list",
     "value": [
     {
       "type": "string",
       "value": "Ride"
     },
     {
       "type": "integer",
       "value": 5
     },
     {
       "type": "string",
       "value": "DecentralCoins"
     }
     ]
    }
   ]
 },
 ...
}

Tuple#

A tuple is an ordered collection of elements. Elements can be of any type. The tuple can contain from \(2\) to \(22\) elements.

Let’s see some tuples:

let x=("Hello DecentralChain",42,true)
x._2

Result: \(42\)

And this one also:

let (a,b,c)=x
c

Result: true

Union#

Union is a data type that unites \(2\) or more data types. Union can combine primitive types, lists, tuples, structures. This type is a very convenient way to work with abstractions. Union(String | Unit) shows that the value is an intersection of these types.

To get a value of a particular type from a Union, you can use:

let valueFromBlockchain = getString("3PHHD7dsVqBFnZfUuDPLwbayJiQudQJ9Ngf", "someKey") # Union(String | Unit)

The simplest example of Union types is given below (please bear in mind that defining custom user types in dApp code will be supported in future versions):

type Human : { firstName: String, lastName: String, age: Int}
type Cat : {name: String, age: Int }

Let’s see anoter example where each element of a List[Int|String] is a string or an integer.

let aList   = [1, 2, "DecentralCoins"]               # List[Int|String]
let bList   = [true,false]                  # List[Boolean]
let joined  = aList ++ bList                # List[Boolean|Int|String]

Pattern Matching#

Let’s revisit the example above:

type Human : { firstName: String, lastName: String, age: Int}
type Cat : {name: String, age: Int }

Union(Human | Cat) is an object with one field, age, but we can use pattern matching like this:
Human | Cat => { age: Int }

This is designed to check a value against value type:

let t = ...               # Cat | Human
t.age                     # OK
t.name                    # Compiler error
let name = match t {      # OK
 case h: Human => h.firstName
 case c: Cat   => c.name
}

Type matching#

This is a mechanism for knowing the type of a transaction:

let amount = match tx {              # tx is a current outgoing transaction
 case t: TransferTransaction => t.amount
 case m: MassTransferTransaction => m.totalAmount
 case _ => 0
}

There are different types of transactions, if a transaction is TransferTransaction or MassTransferTransaction we use the corresponding field, while in all other cases, we will get \(0\).