[m-dev.] subtypes proposal

Mark Brown mark at mercurylang.org
Tue Jan 19 23:39:55 AEDT 2021


Hi Peter,

On Thu, Jan 14, 2021 at 12:55 PM Peter Wang <novalazy at gmail.com> wrote:
>
> Hi,
>
> In this proposal, subtypes are distinct d.u. types that share a data
> representation with a base type, which is a normal d.u. type.
> Subtypes of subtypes are allowed.
> Every term of a given subtype must also be valid term in the supertype
> (recursively, up to the base type), with the same data representation.
>
> A subtype can be introduced like this:
>
>     :- type SUBTYPE =< SUPERTYPE
>         --->    BODY.
>
> The constructor definitions in BODY must be a (non-strict) subset of the
> constructors of the supertype. If the supertype t has constructor
> f(T1, ..., Tn) then a subtype s =< t may have a constructor
> f(S1, ..., Sn) such that Si =< Ti.
>
> TODO: requires further thought
>
>   - higher order types can have inst constraints now
>   - existential class constraints must match exactly?
>
> Any type variable that occurs in SUBTYPE must also occur in SUPERTYPE.
> Any universally quantified type variable that occurs in BODY
> must also occur in SUBTYPE.
>
> Examples:
>
>     :- type empty_list =< list(T)
>         --->    [].
>
>     :- type non_empty_list(T) =< list(T)
>         --->    [T | list(T)].
>
>     :- type non_empty_list_of_foo =< list(foo)
>         --->    [foo | list(foo)].
>
>     :- type maybe_univ
>         --->    none
>         ;       some [T] univ(T).
>
>     :- type yes_univ =< maybe_univ
>         --->    some [T] univ(T).
>
>
> Type conversions
> ----------------
>
> I propose to not introduce any implicit conversion between subtypes and
> supertypes, for a few reasons:
>
>   - there are no implicit type conversions anywhere else in the language
>   - explicit conversions may be easier to understand
>   - I do not want to set myself up to potentially rewrite the type
>     checker ;)
>
> Rather, terms can be coerced between subtype and supertype with an
> explicit operation, say, written like a function call:
>
>     Y = coerce(X)
>
> A coercion from FromType to ToType is type correct if FromType and ToType
> are equivalent after replacing any subtypes with their base types.
>
> For example, if s =< t and non_empty_list(T) =< list(T) then coercions
> between these pairs of types should be accepted by the type checker:
>
>     s <-> t
>     list(s) <-> list(t)
>     non_empty_list(s) <-> non_empty_list(t)
>     list(s) <-> non_empty_list(s)
>     list(s) <-> non_empty_list(t)
>
> Coercions must be mode correct.
> Assume we have a coercion of X:s to Y:t.
> If Xi, Xf, Yi, Yf are the initial and final insts of X and Y
> then the coercion is mode correct iff:
>
>   - Xi is a ground inst (no partial instantiation, no uniqueness)
>
>   - Xf = Xi
>
>   - Yi is free
>
>   - Yf is the glb of Xi and the instantiatedness tree derived from t
>     where all nodes are bound.
>     The glb must exist,
>     Yf must be be ground,
>     and Yf must be a valid inst of t.
>
> Coercing a ground term from subtype to supertype is always allowed.
> We use the mode system to ensure that a coercion from supertype to
> subtype is safe at compile time.
>
> It would probably be useful to also provide a semidet coercion operation
> that can fail at runtime if the term is not valid for the result type.
>
>
> Interaction with modules
> ------------------------
>
> The compiler needs to know the definition of the base type in order to
> construct, deconstruct and coerce terms of a subtype.
>
> If a subtype is defined in the interface section of a module M
> then its supertype must either
>
>  - be defined in the interface section of M, or
>  - be defined in a module imported in the interface section of M.
>
> If a subtype is defined in the implementation section of a module M,
> then its supertype must either
>
>   - be defined in M, or
>   - be defined in the interface section of a module imported by M.
>
> A subtype may be abstractly exported, like other d.u. types.
> Coercions to/from the subtype cannot be checked in modules without
> visibility of the subtype definition (and its supertype),
> and will be rejected at compile time.
>
>
> Comparison to previous proposal
> -------------------------------
>
> Compared to Fergus's 1998 proposal
> (http://lists.mercurylang.org/pipermail/developers/1998-February/002274.html)
> the "subtypes" in this proposal works with polymorphically typed
> (but non polymorphically moded) predicates and containers in a
> straightforward way, without constrained polymorphic modes.

That proposal allowed you to say, for example, that reversing a
non-empty list results in a non-empty list whereas reversing a list
just gives you a list, without just writing two separate reverse
functions. Any chance of allowing something like that?

Mark

>
> I think the declarations of the form
>
>     :- subtype SubType < Type :: Inst.
>
> would have been most useful to provide an inst for a higher order Type,
> but we have a different feature to handle that now.
>
>
> I have attached a copy of a module that demonstrates what code using the
> proposed feature might look like. (Not suggesting that it would be
> better than the cord representation used in the standard library.)
>
> Peter
> _______________________________________________
> developers mailing list
> developers at lists.mercurylang.org
> https://lists.mercurylang.org/listinfo/developers


More information about the developers mailing list