Function overloading in OCaml - ocaml

I have defined some types:
type box = Box of int
type table = Table of int
type compare_result = Lt | Eq | Gt
It seems that in OCaml, we can't define 2 functions with same name but different types of arguments:
let compare (a: box) (b: box): compare_result = (...)
let compare (a: table) (b: table): compare_result = (...)
let res_box = compare (Box 1) (Box 2) in (* which is supposed to call the first funciton *)
let res_table = compare (Table 1) (Table 2) in (* which is supposed to call the second function *)
So could anyone tell me what is the alternative in OCaml to do this? Do we have to name these 2 functions differently?

Yes, the easiest solution is simply to call the functions differently. Allowing programs that do this vastly complicates the type system (not to the point that it isn't possible for experts to design a solution: to the point that you would find it unusable when they do).
Existing solutions for writing a single function compare are the object system in OCaml, and type classes in Haskell (a different extension to the same base type system). But it's much simpler to stay in the simple fragment and to name your functions compare differently.

Related

When should extensible variant types be used in OCaml?

I took a course on OCaml before extensible variant types were introduced, and I don't know much about them. I have several questions:
(This question was deleted because it attracted a "not answerable objectively" close vote.)
What are the low-level consequences of using EVTs, such as performance, memory representation, and (un-)marshaling?
Note that my question is about extensible variant type specifically, unlike the question suggested as identical to this one (that question was asked prior to the introduction of EVTs!).
Extensible variants are quite different from standard variants in term of
runtime behavior.
In particular, extension constructors are runtime values that lives inside
the module where they were defined. For instance, in
type t = ..
module M = struct
type t +=A
end
open M
the second line define a new extension constructor value A and add it to the
existing extension constructors of M at runtime.
Contrarily, classical variants do not really exist at runtime.
It is possible to observe this difference by noticing that I can use
a mli-only compilation unit for classical variants:
(* classical.mli *)
type t = A
(* main.ml *)
let x = Classical.A
and then compile main.ml with
ocamlopt classical.mli main.ml
without troubles because there are no value involved in the Classical module.
Contrarily with extensible variants, this is not possible. If I have
(* ext.mli *)
type t = ..
type t+=A
(* main.ml *)
let x = Ext.A
then the command
ocamlopt ext.mli main.ml
fails with
Error: Required module `Ext' is unavailable
because the runtime value for the extension constructor Ext.A is missing.
You can also peek at both the name and the id of the extension constructor
using the Obj module to see those values
let a = [%extension_constructor A]
Obj.extension_name a;;
: string = "M.A"
Obj.extension_id a;;
: int = 144
(This id is quite brittle and its value it not particurlarly meaningful.)
An important point is that extension constructor are distinguished using their
memory location. Consequently, constructors with n arguments are implemented
as block with n+1 arguments where the first hidden argument is the extension
constructor:
type t += B of int
let x = B 0;;
Here, x contains two fields, and not one:
Obj.size (Obj.repr x);;
: int = 2
And the first field is the extension constructor B:
Obj.field (Obj.repr x) 0 == Obj.repr [%extension_constructor B];;
: bool = true
The previous statement also works for n=0: extensible variants are never
represented as a tagged integer, contrarily to classical variants.
Since marshalling does not preserve physical equality, it means that extensible
sum type cannot be marshalled without losing their identity. For instance, doing
a round trip with
let round_trip (x:'a):'a = Marshall.from_string (Marshall.to_string x []) 0
then testing the result with
type t += C
let is_c = function
| C -> true
| _ -> false
leads to a failure:
is_c (round_trip C)
: bool = false
because the round-trip allocated a new block when reading the marshalled value
This is the same problem which already existed with exceptions, since exceptions
are extensible variants.
This also means that pattern-matching on extensible type is quite different
at runtime. For instance, if I define a simple variant
type s = A of int | B of int
and define a function f as
let f = function
| A n | B n -> n
the compiler is smart enough to optimize this function to simply accessing the
the first field of the argument.
You can check with ocamlc -dlambda that the function above is represented in
the Lambda intermediary representation as:
(function param/1008 (field 0 param/1008)))
However, with extensible variants, not only we need a default pattern
type e = ..
type e += A of n | B of n
let g = function
| A n | B n -> n
| _ -> 0
but we also need to compare the argument with each extension constructor in the
match leading to a more complex lambda IR for the match
(function param/1009
(catch
(if (== (field 0 param/1009) A/1003) (exit 1 (field 1 param/1009))
(if (== (field 0 param/1009) B/1004) (exit 1 (field 1 param/1009))
0))
with (1 n/1007) n/1007)))
Finally, to conclude with an actual example of extensible variants,
in OCaml 4.08, the Format module replaced its string-based user-defined tags
with extensible variants.
This means that defining new tags looks like this:
First, we start with the actual definition of the new tags
type t = Format.stag = ..
type Format.stag += Warning | Error
Then the translation functions for those new tags are
let mark_open_stag tag =
match tag with
| Error -> "\x1b[31m" (* aka print the content of the tag in red *)
| Warning -> "\x1b[35m" (* ... in purple *)
| _ -> ""
let mark_close_stag _tag =
"\x1b[0m" (*reset *)
Installing the new tag is then done with
let enable ppf =
Format.pp_set_tags ppf true;
Format.pp_set_mark_tags ppf true;
Format.pp_set_formatter_stag_functions ppf
{ (Format.pp_get_formatter_stag_functions ppf ()) with
mark_open_stag; mark_close_stag }
With some helper function, printing with those new tags can be done with
Format.printf "This message is %a.#." error "important"
Format.printf "This one %a.#." warning "not so much"
Compared with string tags, there are few advantages:
less room for a spelling mistake
no need to serialize/deserialize potentially complex data
no mix up between different extension constructor with the same name.
chaining multiple user-defined mark_open_stag function is thus safe:
each function can only recognise their own extension constructors.

OCaml Signature with multiple types

I would like to represent some scalar value (e.g. integers or strings)
by either it's real value or by some NA value and later store them
in a collection (e.g. a list). The purpose is to handle missing values.
To do this, I have implemented a signature
module type Scalar = sig
type t
type v = Value of t | NA
end
Now I have some polymorphic Vector type in mind that contains Scalars. Basically, some of the following
module Make_vector(S: Scalar) = struct
type t = S.v list
... rest of the functor ...
end
However, I cannot get this to work. I would like to do something like
module Int_vector = Make_vector(
struct
type t = int
end
)
module Str_vector = Make_vector(
struct
type t = string
end
)
... and so on for some types.
I have not yet worked a lot with OCaml so maybe this is not the right way. Any advises on how to realize such a polymorphic Scalar with a sum type?
The compiler always responds with the following message:
The parameter cannot be eliminated in the result type.
Please bind the argument to a module identifier.
Before, I have tried to implement Scalar as a sum type but ran into
complexity issues when realizing some features due to huge match clauses. Another (imo not so nice) option would be to use option. Is this a better strategy?
As far as I can see, you are structuring v as an input type to your functor, but you really want it to be an output type. Then when you apply the functor, you supply only the type t but not v. My suggestion is to move the definition of v into your implementation of Make_vector.
What are you trying to do exactly with modules / functors? Why simple 'a option list is not good enough? You can have functions operating on it, e.g.
let rec count_missing ?acc:(acc=0) = function
| None::tail -> count_missing ~acc:(acc+1) tail
| _::tail -> count_missing ~acc tail
| [] -> acc ;;
val count_missing : ?acc:int -> 'a option list -> int = <fun>
count_missing [None; Some 1; None; Some 2] ;;
- : int = 2
count_missing [Some "foo"; None; Some "bar"] ;;
- : int = 1

structural comparison of variants

I want to deal with limits on the integer number line.
I would like to have Pervasives.compare treat RightInfinity > Point x for all x, and the inverse for LeftInfinity.
In the ocaml REPL:
# type open_pt = LeftInfinity | Point of int | RightInfinity
;;
# List.sort Pervasives.compare [LeftInfinity; Point 0; Point 1; RightInfinity]
;;
- : open_pt list = [LeftInfinity; RightInfinity; Point 0; Point 1]
but
# type open_pt = LeftInfinity | Point of int | RightInfinity of unit
;;
# List.sort Pervasives.compare [LeftInfinity; Point 0; Point 1; RightInfinity ()]
;;
- : open_pt list = [LeftInfinity; Point 0; Point 1; RightInfinity ()]
"The perils of polymorphic compare" says
Variants are compared first by their tags, and then, if the tags are equal, descending recursively to the content.
Can one rely on any relationship between the order in which variants appear in a type declaration and the order of the tags?
No, you should not rely on that. You should define your own comparison function. Of course, that means you'll have to lift it through datastructures (to be able to compare, say, lists of open_pt), but that's the safe thing to do when you want a domain-specific comparison function.
Note that extended standard libraries such as Batteries or Core provide auxiliary functions to lift comparisons through all common datastructures, to help you extend your domain-specific comparison to any type containing an open_pt.
Edit: Note that you can rely on that, as the ordering of non-constant constructors is specified in the OCaml/C interface. I don't think that's a good idea, though -- what if you need to put closures inside your functor argument type next time?

meaning of warning and type in ML

fun a(list) =
let
val num = length(hd(list))
fun inner(list) =
if num = length(hd(list)) then
if tl(list) = nil then true
else inner(tl(list))
else false
in
if length(hd(list))-1 = length(tl(list)) then inner(tl(list))
else false
end;
this is ml code and I got this warning and type.
stdIn:6.16 Warning: calling polyEqual
val a = fn : ''a list list -> bool
I don't understand about the warning. why it appear and the type. ''a why it has two '? ''?
what is the difference between 'a list list and ''a list list?
Excerpted from ML Hints:
Warning: calling polyEqual [may occur] whenever you use = to
compare two values with polymorphic type.
For example, fun eq(x,y) = (x = y); will cause this warning to be
generated, because x and y will have polymorphic type ''a. This
is perfectly fine and you may ignore the warning. It is not reporting
any kind of semantic error or type error in your code. The compiler
reports the warning because there can be a slight ineffeciency in how
ML tests whether two values of a polymorphic type are equal. In
particular, to perform the equality test, the run-time system must
first determine what types of values you are currently using and then
determine whether the values are equal. The first part (checking the
run-time types) can make the = test slightly slower than if the
types are known ahead of time (such as when we test 3 = 4 and know
that the = test is being applied to integers). However, that is not
something most users of ML ever need to worry about...
To answer your second question,
why it has two '? ''? what is the difference between 'a list list and
''a list list?
''a is the same as 'a, but requires it to be an equality type. An equality type in SML is a type that can be compared using =. Non-equality types cannot be compared using =. When you create a datatype, you can specify whether it is an equality type or not.
dict=val a =[("a",[1,2]),("b",[2,3])] ;
here is the code which has implementation of look up in dictionary
fun look key [] = []
| look key ((a,b)::xs) = if (key =a ) then b else look key xs ;
which gives output as
test1.sml:8.36 Warning: calling polyEqual
It is because it does not know what are the types which are compared so
the below code says that both are string type .
fun look (key:string) [] = []
| look (key:string) ((a:string,b)::xs) = if (key =a ) then b else look (key:string) xs ;

Overloading in Ocaml

I know that OCaml does not support overloading. Then, instead of overloading, what can we do to work this around?
1) use polymorphism instead?
2) give different functions different names?
3) put functions of the same name in different modules?
Which one will work?
It all depends on what you mean by overloading. There are several use cases, such as:
If you want to use the usual infix operators name in a mathematical expression manipulating something else than integers: rebind your operators locally; modules and "local open" can help with that.
module I32 = struct
open Int32
let (+), (-), ( * ), (/), (!!) = add, sub, mul, div, of_int
end
... I32.(x + y * !!2) ...
If you want an operation to be polymorphic in the type of numeric type being used, you need to abstract over such numeric operators. For example the generic fast exponentiation function (by an integer), that can be used on matrices etc.
let rec pow ( * ) one a = function
| 0 -> one
| n -> pow ( * ) (if n mod 2 = 0 then one else one * a) (a * a) (n / 2)
let () = assert (pow ( *.) 1. 2. 3 = 8.)
More generally, yes, the idea is to capture what you want to "overload" on as a set of operators (here infix operators but plain names are fine and often better for readability), and pass around and abstract over dictionaries of those operations -- much like what Haskell type classes are compiled to, in fact.