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:
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:
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:
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:
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:
Comparison operators
In Melange Playground, change your program to this:
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:
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:
Js.log(Float.of_int(foo) == bar);
Js.log(Float.of_int(foo) == bar);
The same-type restriction applies to all the comparison operators:
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:
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:
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:
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:
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:
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
:
<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
[@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:
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.
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.