TL;DR; Java is the closest.
OCaml type system provides parametric polymorphism, that means that a type of value can have type variables (parameters), meaning that a value can have multiple types in a given context. Unlike languages that you mentioned, OCaml also has type inference. OCaml will infer the most general type of value, based on how the value is used in a program, i.e., it will try to find the biggest set of types that match the usage of the value. The property of a type inference system to be able to derive the most general type (the biggest set of types - also called the principal type), is called principality. OCaml uses a type-inference system that is based on Hindley-Milner type system, though it went much father. First of all, OCaml has subtyping, and type inference (that tries to be principal) with subtyping requires some help from a programmer in a form of annotations (otherwise it will infer less general types). This is why OCaml classes have explicit type parameters. If we will forget about subtyping, then classes, objects, functions, and even functors can be considered equal. In fact, they basically have the same runtime representation. In OCaml, we do not make a difference between explicit and implicit type variables. A type of function also have type variables, but since the compiler usually infers the type for us we do not specify them. However, when we define module interfaces, we always explicitly specify type variables, for example, consider the List
module that provides the length
function that has type 'a list -> int
. As you may see, the type variable 'a
is quite explicit here.
Concerning the representation, the Java representation is a correct way of implementing parametric polymorphism. As parametric polymorphism speaks about one value, that has multiple types, not a family of values with different types (it doesn't matter whether they are constructed either at runtime or compile time). This shows an essential difference between C#/C++ generics and OCaml/Java generics. In the latter, we have a value that is generic, while in the former we have a factory of values that has the same interface, but may have different implementations, that is actually an example of ad hoc polymophism. So, in OCaml, when you have a polymorphic function or a data value, it is always only one object, no matter where it is applied, e.g., in List.length [1;2;3]
and List.length ["hello"; "world"]
the same code of the List.length
function is applied to different lists.