[m-dev.] shims

Peter Wang novalazy at gmail.com
Mon Sep 15 15:46:13 AEST 2014


Hi Mark,

On Sun, 14 Sep 2014 12:51:44 +1000, Mark Brown <mark at mercurylang.org> wrote:
> On Thu, Sep 11, 2014 at 5:26 PM, Peter Wang <novalazy at gmail.com> wrote:
> > Hi,
> >
> > When a (standard) library predicate is deprecated, the user faces a
> > choice: update code now and break compatibility with older versions of
> > the library, or face breakage in the future.  We try to give a lot of
> > time in which to make that choice.  In the meantime the compiler keeps
> > nagging, and it's hard to know when to make the break anyway.
> 
> The nagging can be stopped with --no-warn-obsolete. But this isn't
> really about the warnings; I think what you're asking for is the
> ability to target different versions of the same library.

Yes, that's it.

> Here's another approach that I think provides an interesting solution.
> Namely, a top level construct of the following abstract form:
> 
>     if
>         declarations
>     then
>         items
>     else if
>         declarations
>     then
>         items
>     ...
>     else
>         items
>     end if
> 
> These can be nested. The meaning is that we check if there are visible
> items matching the declarations in the conditions. If so, the clauses
> in the first matching then branch are included in the module,
> otherwise the clauses in the else branch are included. Other
> declarations are local to the branch they appear in. In any case, all
> sections are fully statically checked. The then branch is checked with
> the condition's declarations included, replacing any that were already
> visible, while the else branch is checked without them. Predicates or
> functions declared outside this construct can have clauses defined
> inside it; if so, the clauses must be defined in both branches.
> 
> Example (choosing the first concrete syntax that comes to mind):
> 
>     :- import_module set.
>     :- pred portable_singleton_set(T::in, set(T)::out) is det.
>     :- if.
>         :- pred singleton_set(T::in, set(T)::out) is det.
>     :- then.
>         portable_singleton_set(X, Set) :- singleton_set(X, Set).
>     :- else_if.
>         :- pred singleton_set(set(T)::out, T::in) is det.
>     :- then
>         portable_singleton_set(X, Set) :- singleton_set(Set, X).
>     :- else.
>         % If neither of those work it will be a compile time error: no
> clauses for
>         % portable_singleton_set/2, with the `:- else.' as the
> location of the error.
>     :- end_if.
> 
> This is much more verbose than Peter's suggestion, but aside from the
> better checking it's also much more flexible because you can target
> different libraries, not just different versions of the same library.
> For example, if there is a module called fastlib that only some
> developers have in their path, you might write:
> 
>     :- pred algorithm(int::in, int::out) is det,
>     :- if.
>         :- import_module fastlib.           % This can fail.
>         :- pred fastlib.algorithm(int::in, int::out) is det.
>     :- then.
>         algorithm(X, Y) :- fastlib.algorithm(X, Y).
>     :- else.
>         algorithm(X, Y) :- slow_algorithm(X, Y).
>     :- end_if.
> 
> Note that, even though fastlib is imported, we still have to declare
> the predicates from it that are used. This enables the then branch to
> be checked even if the import fails, and also allows a check that
> fastlib itself is the right version.

I like it.

Is it fair to think of each if-then branch declaring something like an
anonymous nested sub-module, with forwarding of predicates and types
from the parent module to the sub-module?

Peter



More information about the developers mailing list