Table of contents for this series of posts: “What is ReasonML?”
In this blog post, we’ll look at ReasonML’s support for booleans, integers, floats, strings, characters and the unit type. We’ll also see a few operators in action.
To explore, we’ll use the interactive ReasonML command line rtop
, which is part of the package reason-cli
(the manual explains how to install it).
rtop
Interactions in rtop
look as follows.
# !true;
- : bool = false
Two observations:
!true;
with a semicolon in order for it to be evaluated.rtop
always prints out the types of the results it computes. That is especially helpful later on, with more complicated types, e.g. the types of functions.Values in ReasonML are statically typed. What does static typing mean?
On one hand, we have the term type. In this context, type means “set of values”. For example bool is the name of the type of all boolean values: the (mathematical) set {false, true}
.
On the other hand, we make the following distinction in the context of the life cycle of code:
Therefore static typing means: ReasonML knows the types of values at compile time. And types are also known while editing code, which supports intelligent editing features.
We have already encountered one benefit of static typing: editing support. It also helps with detecting some kinds of errors. And it often helps with documenting how code works (in a manner that is automatically checked for consistency).
In order to reap these benefits, you should get used to working with types. You get help in two ways:
Ad hoc polymorphism may sound brainy, but it has a simple definition and visible practical consequences for ReasonML. So bear with me.
ReasonML does not currently support ad hoc polymorphism where the same operation is implemented differently depending on the types of the parameters. Haskell, another functional programming language supports ad hoc polymorphism via type classes. ReasonML may eventually support it via the similar modular implicits.
ReasonML not supporting ad hoc polymorphism means that most operators such as +
(int addition), +.
(float addition) and ++
(string concatenation) only support a single type. Therefore, it is your responsibility to convert operands to proper types. On the plus side, ReasonML will warn you at compile time if you forget to do so. That’s a benefit of static typing.
Before we get into values and types, let’s learn comments.
ReasonML only has one way of writing comments:
/* This is a comment */
Conveniently, it is possible to nest this kind of comment (languages with C-style syntax are often not able to do that):
/* Outer /* inner comment */ comment */
Nesting is useful for commenting out pieces of code:
/*
foo(); /* foo */
bar(); /* bar */
*/
Let’s type in a few boolean expressions:
# true;
- : bool = true
# false;
- : bool = false
# !true;
- : bool = false
# true || false;
- : bool = true
# true && false;
- : bool = false
These are integer expressions:
# 2 + 1;
- : int = 3
# 7 - 3;
- : int = 4
# 2 * 3;
- : int = 6
# 5 / 3;
- : int = 1
Floating-point expressions look as follows:
# 2.0 +. 1.0;
- : float = 3.
# 2. +. 1.;
- : float = 3.
# 2.25 +. 1.25;
- : float = 3.5
# 7. -. 3.;
- : float = 4.
# 2. *. 3.;
- : float = 6.
# 5. /. 3.;
- : float = 1.66666666666666674
Normal string literals are delimited by double quotes:
# "abc";
- : string = "abc"
# String.length("ü");
- : int = 2
# "abc" ++ "def";
- : string = "abcdef"
# "There are " ++ string_of_int(11 + 5) ++ " books";
- : string = "There are 16 books"
# {| Multi-line
string literal
\ does not escape
|};
- : string = " Multi-line\nstring literal\n\\ does not escape\n"
ReasonML strings are encoded as UTF-8 and not compatible with JavaScript’s UTF-16 strings. ReasonML’s support for Unicode is also worse than JavaScript’s – already limited – one. As a short-term workaround, you can use BuckleScript’s JavaScript strings in ReasonML:
Js.log("äöü"); /* garbage */
Js.log({js|äöü|js}); /* äöü */
These strings are produced via multi-line string literals annotated with js
, which are only treated specially by BuckleScript. In native ReasonML, you get normal strings.
Characters are delimited by single quotes. Only the first 7 bits of Unicode are supported (no umlauts etc.):
# 'x';
- : char = 'x'
# String.get("abc", 1);
- : char = 'b'
# "abc".[1];
- : char = 'b'
"x".[0]
is syntactic sugar for String.get("x", 0)
.
Sometimes, you need a value denoting “nothing”. ReasonML has the special value ()
for this purpose. ()
has its own type, unit
and is the only element of that type:
# ();
- : unit = ()
In contrast to null
in C-style languages, ()
is not an element of any other type.
Among other things, the type unit
is used for functions with side effects that don’t return anything. For example:
# print_string;
- : (string) => unit = <fun>
The function print_string
takes a string as an argument and prints that string. It has no real result.
ReasonML’s standard library has functions for converting between the basic types:
# string_of_int(123);
- : string = "123"
# string_of_bool(true);
- : string = "true"
All of the conversion functions are named as follows.
«outputType»_of_«inputType»
The following are comparison operators. They are part of the few operators that work with several types (they are polymorphic).
# 3.0 < 4.0;
- : bool = true
# 3 < 4;
- : bool = true
# 3 <= 4;
- : bool = true
You cannot, however, mix operand types:
# 3.0 < 4;
Error: Expression has type int but expected type float
ReasonML has two equality operators.
Double equals (equality by value) compares values and does so even for reference types such as lists.
# [1,2,3] == [1,2,3];
- : bool = true
In contrast, triple equals (equality by reference) compares references:
# [1,2,3] === [1,2,3];
- : bool = false
==
is the preferred equality operator (unless you really want to compare references).