Skip to content

Numeric Types

OCaml’s numeric types work differently than in JavaScript. The first thing you might notice is that OCaml makes a clear and hard distinction between integer and float types. For example:

reason
let foo = 42;   // int
let bar = 42.1; // float
Js.log(foo);
Js.log(bar);
let foo = 42;   // int
let bar = 42.1; // float
Js.log(foo);
Js.log(bar);

Note that you didn’t have to write the types yourself, OCaml is able to infer the types from the literals. This would also work:

reason
let foo: int = 42;
let bar: float = 42.1;
Js.log(foo);
Js.log(bar);
let foo: int = 42;
let bar: float = 42.1;
Js.log(foo);
Js.log(bar);

Melange Playground

Melange Playground is an interactive environment for running OCaml code and seeing its output. Paste this into the source code editor on the left side:

reason
let foo = 42;
let bar = 42.1;
Js.log(foo);
Js.log(bar);
let foo = 42;
let bar = 42.1;
Js.log(foo);
Js.log(bar);

In the bottom right panel, you can see the console output, which should be:

42
42.1
42
42.1

In the top right panel, you can see the JavaScript generated by the Melange compiler:

// Generated by Melange


console.log(42);

console.log(42.1);

var foo = 42;

var bar = 42.1;

export {
  foo ,
  bar ,
}
/*  Not a pure module */
// Generated by Melange


console.log(42);

console.log(42.1);

var foo = 42;

var bar = 42.1;

export {
  foo ,
  bar ,
}
/*  Not a pure module */

Note that the console.log invocations are given constants instead of the variables. That’s a nifty optimization done by Melange.

Another sweet feature is that you can hover over the variables and see their types. Try it out, and you’ll see that foo has type int while bar has type float.

The Problems pane in the bottom left corner shows error mesages when appropriate. At the moment, it shows No problems. Try type-annotating bar with int and see what happens:

reason
let foo = 42; // int
let bar: int = 42.1; // float
Js.log(foo);
Js.log(bar);
let foo = 42; // int
let bar: int = 42.1; // float
Js.log(foo);
Js.log(bar);

The Problems pane should now show something like this:

Line 2, 15:
  Error This expression has type float but an expression was expected of type int
Line 2, 15:
  Error This expression has type float but an expression was expected of type int

Sharing code snippets

Melange Playground is also an excellent way to share OCaml code snippets with your friends! As you type in the source editor, it will store your code in the code query string parameter of the URL. For example, here’s a link to the snippet we started with:

https://melange.re/v4.0.0/playground/?language=Reason&code=bGV0IGZvbyA9IDQyOwpsZXQgYmFyID0gNDIuMTsKSnMubG9nKGZvbyk7CkpzLmxvZyhiYXIpOw%3D%3D&live=off

Comparison operators

In Melange Playground, change your program to this:

reason
let foo = 42;   // int
let bar = 42.0; // float
Js.log(foo == bar);
let foo = 42;   // int
let bar = 42.0; // float
Js.log(foo == bar);

You’ll get a compiler error for the last line that says

This expression has type float but an expression was expected of type int
This expression has type float but an expression was expected of type int

Unlike JavaScript, there are no implicit conversions in OCaml. Therefore you cannot expect to compare an integer and a float together, unless you convert one of the values so that both values have the same type. To make the last line compile, you can change it to:

reason
Js.log(foo == Int.of_float(bar));
Js.log(foo == Int.of_float(bar));

Another way to fix the last line is to convert foo from an int to a float:

reason
Js.log(Float.of_int(foo) == bar);
Js.log(Float.of_int(foo) == bar);

The same-type restriction applies to all the comparison operators:

reason
Js.log(3 < 33);
Js.log(44. > 4.);
Js.log(5 <= 55);
Js.log(66. >= 6.);
Js.log(3 < 33);
Js.log(44. > 4.);
Js.log(5 <= 55);
Js.log(66. >= 6.);

Arithmetic operators

What about addition? From what you’ve already seen, you can probably guess that Js.log(42 + 16.0) won’t compile. However, you may be surprised to discover that Js.log(42.0 + 16.0) also won’t compile! That’s because OCaml uses separate arithmetic operators for floats. What will compile is this:

reason
Js.log(42.0 +. 16.0); // prints 58
Js.log(42.0 +. 16.0); // prints 58

Here are examples of the other arithmetic operators, try them out in the playground:

reason
Js.log(66.0 -. 6.0);
Js.log(66.0 *. 6.0);
Js.log(66.0 /. 6.0);
Js.log(66.0 -. 6.0);
Js.log(66.0 *. 6.0);
Js.log(66.0 /. 6.0);

Notice that all float arithmetic operators end with . (period), just like float literals.

They’re just Number

Underneath the covers, foo and bar are both instances of JavaScript’s Number type:

reason
let foo = 42;   // int
let bar = 42.0; // float
Js.log(Js.typeof(foo)); // prints "number"
Js.log(Js.typeof(bar)); // prints "number"
let foo = 42;   // int
let bar = 42.0; // float
Js.log(Js.typeof(foo)); // prints "number"
Js.log(Js.typeof(bar)); // prints "number"

Refer to the Melange docs for a complete rundown of how OCaml types get translated to JavaScript types.

Widgets in the Playground

Melange Playground can also render ReasonReact components! Click the Live button next to the JavaScript output button. Now paste in the code to render the Counter component from the previous chapter:

re
module Counter = {
  [@react.component]
  let make = () => {
    let (counter, setCounter) = React.useState(() => 0);

    <div
      style={ReactDOM.Style.make(
        ~padding="1em",
        ~display="flex",
        ~gridGap="1em",
        (),
      )}>
      <button onClick={_evt => setCounter(v => v - 1)}>
        {React.string("-")}
      </button>
      <span> {counter |> Int.to_string |> React.string} </span>
      <button onClick={_evt => setCounter(v => v + 1)}>
        {React.string("+")}
      </button>
    </div>;
  };
};

switch (ReactDOM.querySelector("#preview")) {
| None => Js.log("Failed to start React: couldn't find the #preview element")
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <Counter />);
};
module Counter = {
  [@react.component]
  let make = () => {
    let (counter, setCounter) = React.useState(() => 0);

    <div
      style={ReactDOM.Style.make(
        ~padding="1em",
        ~display="flex",
        ~gridGap="1em",
        (),
      )}>
      <button onClick={_evt => setCounter(v => v - 1)}>
        {React.string("-")}
      </button>
      <span> {counter |> Int.to_string |> React.string} </span>
      <button onClick={_evt => setCounter(v => v + 1)}>
        {React.string("+")}
      </button>
    </div>;
  };
};

switch (ReactDOM.querySelector("#preview")) {
| None => Js.log("Failed to start React: couldn't find the #preview element")
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <Counter />);
};

Note that we explicitly defined a module for the Counter component this time because we can’t use multiple files in Melange Playground.

Let’s make the counter look a little more impressive. Add a new module called Styles which contains all the styles we want to use:

re
module Styles = {
  // Alias the function to save on keystrokes
  let make = ReactDOM.Style.make;

  let root =
    make(
      ~fontSize="2em",
      ~padding="1em",
      ~display="flex",
      ~gridGap="1em",
      ~alignItems="center",
      (),
    );

  let button =
    make(
      ~fontSize="1em",
      ~border="1px solid white",
      ~borderRadius="0.5em",
      ~padding="0.5em",
      (),
    );

  let number = make(~minWidth="2em", ~textAlign="center", ());
};
module Styles = {
  // Alias the function to save on keystrokes
  let make = ReactDOM.Style.make;

  let root =
    make(
      ~fontSize="2em",
      ~padding="1em",
      ~display="flex",
      ~gridGap="1em",
      ~alignItems="center",
      (),
    );

  let button =
    make(
      ~fontSize="1em",
      ~border="1px solid white",
      ~borderRadius="0.5em",
      ~padding="0.5em",
      (),
    );

  let number = make(~minWidth="2em", ~textAlign="center", ());
};

And then update the JSX in our make function to use the style objects in Styles:

re
<div style=Styles.root>
  <button style=Styles.button onClick={_evt => setCounter(v => v - 1)}>
    {React.string("-")}
  </button>
  <span style=Styles.number>
    {counter |> Int.to_string |> React.string}
  </span>
  <button style=Styles.button onClick={_evt => setCounter(v => v + 1)}>
    {React.string("+")}
  </button>
</div>;
<div style=Styles.root>
  <button style=Styles.button onClick={_evt => setCounter(v => v - 1)}>
    {React.string("-")}
  </button>
  <span style=Styles.number>
    {counter |> Int.to_string |> React.string}
  </span>
  <button style=Styles.button onClick={_evt => setCounter(v => v + 1)}>
    {React.string("+")}
  </button>
</div>;

Here’s the playground link for the fully-styled Counter component.


W00t! You are now empowered to use numbers in your OCaml programs.

Overview

  • Integer and float are separate types in OCaml, but both translate to JavaScript’s Number type once your program is compiled
  • Comparison operators expect the values on both sides to be the same type
  • Integer arithmetic operators are the same as in JavaScript, but float arithmetic operators are different (they end with .) and only accept floats as inputs
  • Melange Playground is a great way to play around with short OCaml programs and has many helpful features:
    • Display console output
    • Display compiled JavaScript output
    • Display error messages when your code isn’t compiling
    • Show type hints on hover
    • Share code snippets via URL
    • Render ReasonReact components

Exercises

1. Convert the Counter component we created in the previous chapter to use float instead of integer. Make the - button decrement by 0.5 and the + button increment by 1.5.

Solution
re
[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0.0);

  <div
    style={ReactDOM.Style.make(
      ~padding="1em",
      ~display="flex",
      ~gridGap="1em",
      (),
    )}>
    <button onClick={_evt => setCounter(v => v -. 0.5)}>
      {React.string("-")}
    </button>
    <span> {counter |> Float.to_string |> React.string} </span>
    <button onClick={_evt => setCounter(v => v +. 1.5)}>
      {React.string("+")}
    </button>
  </div>;
};
[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0.0);

  <div
    style={ReactDOM.Style.make(
      ~padding="1em",
      ~display="flex",
      ~gridGap="1em",
      (),
    )}>
    <button onClick={_evt => setCounter(v => v -. 0.5)}>
      {React.string("-")}
    </button>
    <span> {counter |> Float.to_string |> React.string} </span>
    <button onClick={_evt => setCounter(v => v +. 1.5)}>
      {React.string("+")}
    </button>
  </div>;
};

2. Add an int64 value to your program in Melange Playground:

reason
let baz = 42_000_000_000L; // int64
Js.log(baz);
let baz = 42_000_000_000L; // int64
Js.log(baz);

Note the use of underscores to make the large number more readable. What is the JavaScript representation of int64?

Solution

JavaScript’s Number type doesn’t have enough precision to represent in64 values. They’re instead represented by an array of two numbers [high, low], where high is signed, low is unsigned.

3. How do you add two int64 values?

Hint

Take a look at the standard library’s Int64 module.

Solution

You can add two int64 values using Int64.add, e.g.

reason
let result = Int64.add(42L, 16L);
Js.log(result); // prints [0,58]
let result = Int64.add(42L, 16L);
Js.log(result); // prints [0,58]

View source code and demo for this chapter.