OCaml - Signatures, Modules and Types - ocaml

I have a basic question about types in signatures. If I have two ocaml files like:
istr.ml
type t = int
let get_string f v = f v
and fstr.ml
type t = float
let get_string f v = f v
and a signature
stri.mli
module type STR =
sig
type t
val get_string: (t -> string) -> t -> string
end
What is the type t in the above signature? Is it polymorphic?

it is an abstract type that hides the implementation and prevents the user to use it directly. see module system or RWO
But I am not sure that your current examples will work as your code examples does not seem to be in modules.

Related

Polymorphic type declaration in OCaml

when I write a type that e.g. only accepts strings: let type t1 = string
I can do let name : t1 = "A", but not let age : t1 = 1
But when I want to have a generic type that accepts any data type I have to do this: let type 'a t2 = 'a So I can do both let name : t2 = "A", let age : t2 = 1.
But why do I have to write let type 'a t2 = 'a instead of let type t2 = 'a?
The form
let type t1 = string
is not syntactically valid OCaml.
I imagine that you meant:
type t1 = string
Similarly, with the type constructor t2 defined as
type 'a t2 = 'a
then
let x : t2 = "hi"
is a type error because t2 is not a type but a type constructor of arity one.
The closest valid definition would be:
let x: string t2 = "hi"
which is equivalent
let x: 'a t2 = "hi"
because the type variable 'a is equated to 'a = string when inferring the type of x. But 'a t2 is an abbreviation for 'a, thus the above is still the same as
let x : string = "hi"
At a higher level, there is no useful generic type that accepts any data¹. Indeed, if it existed such type would break the type system.
¹ There are an advanced feature (GADTs or record with polymorphic fields) that allows to either create black-hole types that can carry a data of any kind but forbids any use of the data or types without any values of this type. However, it is probably better to first familiarize yourself with the core part of the type system before exploring those area.
To add to #octachron's answer, you can think of a declaration like type 'a t = 'a as the declaration of a function over types: given a type 'a, t will produce a new type 'a t (which in this case is just 'a). So you need the 'a just like you would need to declare the parameters in a function.
This shouldn't be confused with the occurrences of 'a in an explicit typing form like expr : 'a t. In this case, OCaml thinks of 'a as a type variable, which may or may not denote polymorphism. If you actually wanted to have polymorphism as is the polymorphic type, you'd have to introduce a polymorphic type variable either with 'a. ... or with a type a:
let id : 'a. 'a -> 'a = fun x -> x
let id (type a) (x : a) : a = x
However, note that these forms are not quite equivalent, there are subtle differences, and they are in any case different (in general, not in this specific example) from let id : 'a -> 'a = fun x -> x or let id (x : 'a) : 'a = x.

Explanations of OCaml Warnings

I've been reading through the list of OCaml warnings and am unsure what some of them mean (no examples are provided). Specifically, I'd like to understand:
Examples of code that trigger the following warnings (I think my interpretation of what each warning means is different from what it actually means because I am finding it difficult to produce cases that trigger the warnings that aren't outright language errors):
5. Partially applied function: expression whose result has function type and is ignored.
6. Label omitted in function application.
28. Wildcard pattern given as argument to a constant constructor.
59. Assignment to non-mutable value
What an "ancestor variable" and an "extension constructor" are:
36. Unused ancestor variable.
38. Unused extension constructor.
What these mean:
61. Unboxable type in primitive declaration
62. Type constraint on GADT type declaration
To complete the list:
A wild card pattern can be used as argument of an argument-less constructor
type t = A
let f x = match x with A _ -> 0
Warning 28: wildcard pattern given as argument to a constant constructor
This warning is raised when one names an inherited class without using it:
class c = object end
class d = object
inherit c as super
end
Warning 36: unused ancestor variable super.
An extension construction is a constructor added to an extensible sum type like exn
module M:sig end = struct
type exn += Unused
end
Warning 38: unused exception Unused
With recent versions of OCaml, it is possible to avoid boxing record with only one field or variant types with one constructor. This unboxing currently requires an annotation
type t = I of int [##unboxed]
However, the default representation may change in the future.
This change is transparent except for the FFI. Which means that external are paticularly brittle if they involve type without an annotation:
type t = I of int
external id: t -> t = "%identity"
Warning 61: This primitive declaration uses type t, which is unannotated and
unboxable. The representation of such types may change in future
versions. You should annotate the declaration of t with [##boxed]
or [##unboxed].
Type constraints does not apply on GADT arguments when defining a variant type. For instance, in
type 'a t =
| A: 'a -> float t
| B of 'a
constraint 'a = float
Warning 62: Type constraints do not apply to GADT cases of variant types.
The warning explains that B [] is an error, whereas A[] is fine.
This warning is an internal flambda warning, which warns that a value that flambda conjectured to be non-mutable was, in-fact, mutable. This warning should not be raised in normal circumstances.
Here's an example for warning 5:
# let f a b = a + b;;
val f : int -> int -> int = <fun>
# ignore (f 3);;
Warning 5: this function application is partial,
maybe some arguments are missing.
- : unit = ()
Warning 6 is disabled by default. If you enable it, it's easy to produce:
$ rlwrap ocaml -w +6
OCaml version 4.06.1
# let f ~a = a * 10;;
val f : a:int -> int = <fun>
# f 3;;
Warning 6: label a was omitted in the application of this function.
- : int = 30
The rest are beyond what I can figure out without looking at compiler sources. Maybe an expert or two will show up who can give examples for them.

Can we define a function with 0 argument in ocaml?

In other languages, we can have a function which takes no arguments. Can we have 0 argument function in ocaml?
Functions in OCaml have exactly one argument (ignoring complications due to optional arguments). So, you can't have a function with no arguments.
As #alfa64 says, you could consider a simple value as a function with no arguments. But it will always have the same value (which, in fact, makes it similar to a pure function).
If you want to write a function that doesn't actually require any arguments (one that has side effects, presumably), it is traditional to use () as its argument:
# let p () = Printf.printf "hello, world\n";;
val p : unit -> unit = <fun>
# p ();;
hello, world
- : unit = ()
#
In OCaml functions always have an arguments. Therefore, we might wonder how to translate the following say_hello C function in OCaml:
void
say_hello()
{
printf("Hello, world!\n");
}
There is a special type unit in OCaml which has only one value, written as (). While it might look odd and useless, it adds regularity to the language: a function not needing a specific argument can just take an argument of type unit, a function not returning a useful value usually returns a value of type unit. Here is how to translate the above say_hello function to OCaml:
# let say_hello () = print_endline "Hello, world!";;
val say_hello : unit -> unit = <fun>
Incidentally, template based meta-programming would be much easier in C++ if there were no type void but a similar unit type instead. It is quite common to treat separately functions having no arguments in template specialisations.
Object methods, while being similar to functions, do not require an argument.
# class example =
object
method a =
print_endline "This demonstrates a call to method a of class example"
end;;
class example : object method a : unit end
# let x = new example;;
val x : example = <obj>
# x # a ;;
This demonstrates a call to method a of class example
- : unit = ()
Instead of
let foo n = 55
You just
let foo = 55
And then call foo wherever.

Coercing type abbreviated records

Why can't I coerce record types in OCaml? Base types like int works fine.
Below is an example where I construct a base module M which I include in module A. M.t is type abbriviated in A. As long as M.t is int, I can do A.t' :> M.t. When I change it to {i : int}, the compiler says it's not a subtype. I'm guessing there is a very specific reason for this?
module M = struct
type t = {i : int}
let make () = {i = 10}
end
module A : sig
include module type of M
type t' = private t
val make : unit -> t'
end = struct
include M
type t' = t
end
In the toplevel:
(A.make() :> M.t);;
Error: Type A.t' is not a subtype of M.t
That's because A.t' has no relation to M.t, because include does not "preserve" equality, it just literally duplicates the module structure (or signature) and inlines it in place (as fresh types and values). So type M.t doesn't have any relation to A.t and therefore to A.t' (and different record types do not have structural subtyping defined like say objects or modules).
Obvious fix is type t' = private M.t.
UPDATE
It appears the above is not fully correct, because type t' = private M.t in signature and include M type t' = t in implemention do typecheck, so include M preserves the equality (otherwise it couldn't match the signature type t' = private M.t), unlike copypasting the contents of M in the place of include M. But this "obviously" doesn't hold for include module type of which creates fresh types..

how to access a type defined in one .ml file in another .ml file

In a.ml a record type t is defined and is also defined transparently
in a.mli, i.e. in d interface so that the type definition is available
to all other files.
a.ml also has a function, func, which returns a list of t.
Now in another file, b.ml i m calling func, now obviously ocaml
compiler wud nt be able to infer d type of objects stored in d list,
for compiler its just a list. so in b.ml, i hav something like dis,
let tlist = A.func in
let vart = List.hd tlist in
printf "%s\n" vart.name (*name is a field in record t*)
Now here i get a compiler error sayin "Unbound record field label
name" which makes sense as compiler can't infer d type of vart.
my first question: how do I explicitly provide d type of vart as t
here?
i tried doing "let vart:A.t = " but got the
same error.
I also tried creating another function to fetch the first element of d
list and mentioning return type as A.t, but then i got the "Unbound
value A.t". I did this:
let firstt = function
[] -> 0
| x :: _ -> A.t x ;;
The problem is compiler is unable to recognize A.t (a type) in b.ml
but is able to recognize function A.func. If I remove A.t from the
b.ml, i don'get any compiler errors.
The compiler is able to recognize A.t to designate type t from file a.ml.
On the other hand, t x where t is a type and x a variable is not a valid expression. This is the source of your problem. The compiler looks for the name t in the variables exported by a.ml. Since it cannot find this name used for a variable, it reports an error.
Now for what you were trying to do:
type annotations are not hints to the compiler in OCaml. It has an algorithm to infer the (most general, with some exceptions I am not entering into) type and then it checks that your annotation is at least a particularization of the most general type. The type annotation you provide will never (again, with some possible exceptions that do not concern you) make it change its mind.
To access in file b.ml the field f of a record r of type A.t, type r.A.f.
let tlist = A.func in
let vart = List.hd tlist in
printf "%s\n" vart.name (*name is a field in record t*)
Here tlist is a function, I suppose it is of type 'a -> A.t list, but you are applying an List.hd on that function when you write let vart = List.hd tlist.
You should provide the argument for your function to get the list of A.t for example let tlist = A.func (your_argument-s-_here), I guess this is the source of your error, the compiler is able to infer types between modules.