Created
October 21, 2020 01:44
-
-
Save dbramucci/d3f573bd0f489fb06e32d489d5542830 to your computer and use it in GitHub Desktop.
Example of using OCaml's module system for adhoc polymorphic infix operators.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
If you want, you can replace all | |
+? | |
with | |
+ | |
and this will still work. | |
It's just that the default (+) for `int` will be shadowed after the | |
let Open Num in | |
line | |
*) | |
(* Resources used during writing: | |
* https://ocaml.org/learn/tutorials/modules.html | |
Used for basic module syntax/intro | |
* https://medium.com/@huund/do-not-open-6e5d3070c118 | |
Used to discover syntax for local module imports | |
* https://caml.inria.fr/pub/docs/manual-ocaml/moduleexamples.html | |
Contained the example of with type element = Elt.t for unabstracting types | |
* https://try.ocamlpro.com/ | |
Used to interactively develop and run this sample. | |
*) | |
(* analogous to typeclass Numbertype Num where *) | |
module type Number_type = | |
sig | |
type num | |
val (+?) : num -> num -> num | |
val one : num | |
end | |
(* Analogous to instance (Number_type Int) *) | |
module Integer : (Number_type with type num = int) = | |
struct | |
type num = int | |
let (+?) = (+) | |
let one = 1 | |
end | |
(* | |
The | |
with type num = float | |
is there to allow the caller to use the result of (+?) as an int | |
Otherwise, it would be an abstract type | |
(like a Haskell module that doesn't export the constructors for a Number newtype) | |
And you couldn't do anything other than addition with the result. | |
Nor could you plug in ints constructed outside of the module | |
*) | |
module Float : (Number_type with type num = float) = | |
struct | |
type num = float | |
let (+?) = (+.) | |
let one = 1.0 | |
end | |
(* Here is how we can use the module to write monomorphic code *) | |
let rec fib_int n = | |
(* | |
The let open brings all functions in the Integer module into the local scope | |
*) | |
let open Integer in | |
match n with | |
| 0 -> one | |
| 1 -> one | |
| n -> fib_int (n - 1) +? fib_int (n - 2) | |
(* | |
Same as fib_int except we open Float to indicate we want float's | |
instance of (+?) | |
very roughly analogous to (+) @Float | |
compared to (+) @Int | |
*) | |
let rec fib_float n = | |
let open Float in | |
match n with | |
| 0 -> one | |
| 1 -> one | |
| n -> fib_float (n - 1) +? fib_float (n - 2) | |
(* | |
To write code generic over the number type | |
analogous to fib :: Num a => Int -> a | |
we have to use a Functor to let the caller choose our instance. | |
*) | |
module Fib = | |
functor (Num: Number_type) -> | |
struct | |
let rec fib n = | |
let open Num in | |
match n with | |
| 0 -> one | |
| 1 -> one | |
| n -> fib (n - 1) +? fib (n - 2) | |
end | |
let fib4_int = fib_int 4 | |
let fib5_int = let open Fib(Integer) in fib 5 | |
let fib4_float = fib_float 4 | |
let fib5_float = let module FloatFib = Fib(Integer) in FloatFib.fib 5 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment