Polymorphism in OCaml - ad hoc,parametric, inclusion/subtyping

后端 未结 3 1972
鱼传尺愫
鱼传尺愫 2021-02-10 08:46

I am having a problem understanding the different types of polymorphism, specifically in regards to OCaml. I understand that polymorphism allows for multiple types in OCaml den

3条回答
  •  北恋
    北恋 (楼主)
    2021-02-10 09:21

    Here's an approximation.

    Ad-hoc polymorphism usually refers to being able to declare the same name (usually a function) with different types, e.g. + : int -> int -> int and + : float -> float -> float in SML. These are different functions, and they can act in totally different ways, but the compiler or interpreter chooses the appropriate one depending on the context. I can't think of any instances of ad-hoc polymorphism in OCaml. It is common in C++ and Java, however.

    Parametric polymorphism is when a single function can work with an argument of any type due to not trying to look into that argument's structure. For example, cons : 'a -> 'a list -> 'a list is able to prepend a value v of any type to a list of values of the same type, because it does not matter to cons what the structure (layout) of v is, or what operations it supports. In C terms, cons does not need to "dereference" the pointer, or perform any operation on v that is specific to the actual type of v. Note that unlike in ad-hoc polymorphism, cons has to act the same way for all types. Parametric and ad-hoc polymorphism are thus in a way natural "opposites" of each other. Parametric polymorphism is responsible for the great majority of instances of polymorphism in OCaml.

    Subtype polymorphism is when you can use values of type t where values of type u are expected. This could be because type t supports all the operations of type u, or because t's structure can be used where u is expected. Examples of this would be subclassing (perhaps a Bus can be used wherever a Vehicle can), or polymorphic variants (you can use 'A | 'B where 'A | 'B | 'C is expected).

    EDIT per comment

    Note, however, that subtyping has to be requested explicitly in OCaml. If you have, for example, a function f : u -> int, and you want to apply it to v : t where t is a subtype of u, you have to write f (v :> u). The (v :> u) syntax is a type coercion.

    OCaml also supports row polymorphism, which is a form of parametric polymorphism with constraints. If f is instead f : #u -> int (for object types) or f : [< u] -> int (for polymorphic variants), the #u/[< u] syntax represents a type variable, similar to 'a, but which can only be replaced with the respective "subtypes" of u (in the restricted sense that they can support more fields/less constructors, respectively). Then, you can do f v without the coercion. OCaml automatically infers types that use row polymorphism for many expressions involving polymorphic variants and objects, but you have to write the types explicitly if you are creating a signature.

    There are more usages and considerations to row polymorphism. I've neglected the actual row variables and additional syntax, and only described something that looks like bounded quantification (as in Java generics). More detailed and accurate discussion of row polymorphism, its name, and/or its formalism, is perhaps best saved for separate questions.

提交回复
热议问题