I want to have a shared library between OCaml native and js_of_ocaml. The problem is that I am writing a software that will do financial calculations and I’d like to use decimal math (something like Zarith https://github.com/ocaml/Zarith) on the backend - where real calculations happen; whereas I can go perfectly fine with floats on the frontend.
What should I do in order to not have to rewrite this library two times?
Thanks! I didn’t understand this example, but I’ll read about functors and come back for it, but regarding this:
Do you recommend that I implement a pure OCaml module for rational math using BigInt (reimplement portions of Zarith) or is there something easier that I’m missing?
(Num, BigInt, Ratio) are the equivalent of (Zarith, Z, Q), so rational numbers are available in both. Zarith is mostly better in every way (as long as you accept to depend on GMP).
Num is not pure OCaml, js_of_ocaml just provides js primitives to replace the C ones. In theory, this could be done for zarith as well, but nobody has done it yet.
It seems that, since the Num library was split from the main distribution in OCaml 4.06, there is no longer an HTML version of the API online. However, the API for the version in OCaml 4.05 is still online here:
# #require "zarith";;
~/.opam/4.05.0/lib/zarith: added to search path
~/.opam/4.05.0/lib/zarith/zarith.cma: loaded
# #install_printer Q.pp_print;;
# let r = Q.( 102//10 - 24//10);;
val r : Q.t = 39/5
# Q.to_float r;;
- : float = 7.8
Note that there is no function to parse floating point values, you have to convert them to fractions yourself. There is Q.of_float but that may not give you what you want since the value is rounded to floating point precision:
They are not properly speaking overloadings, the definitions of, here, Q shadow the existing ones when a local open is performed (Q.(e) is equivalent to let open Q in e, i.e., opening the module Q for the expression e).
First note that the returned value is not the same, so both operators (Q.(//) and // are different). Moreover, // must be defined in a module that you opened because it is not a standard operator.
I get that they are different, what I don’t get is how the compiler knows which one of the two to use when inside the Q.( ), given OCaml’s eager nature.
OK, here is what I have for the part where I need to convert strings to fractions, using Angstrom. I am not sure i have to use angstrom, but it works for me, as it’s either way part of my parser :
open Angstrom
let is_digit = function
| '0' .. '9' -> true
| _ -> false
let integer =
take_while is_digit
>>= fun s ->
return (`Int s)
let number =
list [
option (`Char '+') ((char '-') >>= fun c -> return (`Char c));
integer;
peek_char >>= function
| None -> return (`Int "0")
| Some '.' -> (char '.' *> integer)
| Some _ -> return (`Int "0")
]
>>= function
| [`Char sign; `Int whole; `Int part] ->
let signed_whole = match sign with
| '-' -> "-" ^ whole
| '+' -> whole
| _ -> assert false in
let len_part = (String.length part) in
let denom = (Int.pow 10 len_part) in
return (`Number (int_of_string (signed_whole ^ part) , denom))
| _ -> fail "Number parser shouldn't happen"
This gives me something like:
utop # Angstrom.parse_string Lklparser.number "-10.2";;
- : ([> `Number of int * int ], string) result = Result.Ok (`Number (-102, 10))
Of course, this doesn’t reduce the fraction, but Zarith doesn’t mind and does that for me:
utop # Q.(-102//10);;
- : Q.t = -51/5
My question now is why is something like this not part of Zarith itself, as this is quite hard to do for an OCaml newbie. Also, would something like this be a good pull request? I imagine not, as they wouldn’t want to include Angstrom as a dependency, and also probably my code is too naive for general usage…
But maybe something like this could be at least included in the docs?
Special shoutout to @Drup