[m-users.] Existential higher-order type

Volker Wysk post at volker-wysk.de
Tue Oct 11 16:21:29 AEDT 2022


Am Dienstag, dem 11.10.2022 um 05:05 +1100 schrieb Mark Brown:
> On Tue, Oct 11, 2022 at 2:27 AM Volker Wysk <post at volker-wysk.de> wrote:
> > 
> > Am Dienstag, dem 11.10.2022 um 01:31 +1100 schrieb Mark Brown:
> > > > I have this:
> > > > 
> > > > :- type checker
> > > >     ---> some [T] (
> > > >        checker( pred(T::in) is det ) => argument(T) ).
> > > > 
> > > > How can you call the predicate that is stored inside, provided it has the
> > > > expected type?
> > > 
> > > Just deconstruct it and call it :-)
> > > 
> > > (If you're getting an error from doing that, the problem might be elsewhere.)
> > 
> > Here's what I've tried. But this can't work:
> > 
> >    MChecker = yes({ checker(Checker), _ }),
> >    Checker(ArgDesc, Val)    % <-- line 1371
> 
> What's the type and inst of Checker? It doesn't match the declaration you gave.

I've simplified it. Here's the full story:

:- type checker(Arg)
    ---> some [T] ( checker( 
       pred(argdesc(Arg)::in, T::in) is det ) => argument(T) ).

:- type argdesc(Arg)
    ---> some [T] argdesc(
        argdesc_arg :: Arg,
        argdesc_valname :: maybe({ maybe(string), property(Arg) }),
        argdesc_long :: list(string),
        argdesc_short :: list(char),
        argdesc_default :: maybe({ T, property(Arg) }),
        argdesc_min :: maybe({ int, property(Arg) }),
        argdesc_max :: maybe({ int, property(Arg) }),
        argdesc_checker :: maybe({ checker(Arg), property(Arg) }),
        argdesc_env :: maybe({ string, property(Arg) })
    ).

And the call:

         MChecker = ArgDesc ^ argdesc_checker,
         (
             MChecker = yes({ checker(Checker), _ }),
             Checker(ArgDesc, Val)
         ;
             MChecker = no
         )

So, the type of the "Checker" variable should be:

some [T] (pred(argdesc(Arg), T) is det ) => argument(T))

And the inst should be:

pred(in, in) is det

> > Here Val has the polymorphic type T. This is the declaration of my function
> > which tries to call that checker:
> > 
> > :- func arg_value(arguments(Arg), Arg) = T <= argument(T).
> > 
> > Because Val can have any type (within the type class), the call
> > "Checker(ArgDesc, Val)" can't work. I get this compiler error:
> > 
> > ...
> > colipa.m:1371:   in argument 3 (i.e. argument 2 of the called predicate) of
> > colipa.m:1371:   higher-order predicate call:
> > colipa.m:1371:   type error: variable `Val' has type
> > colipa.m:1371:     some [T_2] (T_2),
> > colipa.m:1371:   expected type was
> > colipa.m:1371:     some [T_21] (T_21).
> > colipa.m:1371:   The partial type assignment was:
> > ...
> > 
> > What I want to do, is to check if the types of the called predicate (a so-
> > called "checker") and the type of the "Val" variable match (such that Val
> > has the type of the existentially quantified first argument of the checker).
> > When the types match, the checker is to be run. When not, an exception is to
> > be thrown.
> 
> You can use dynamic_cast for this sort of thing, but I can't say where
> without knowing how you've declared the checker.

Hmm...  Here's the declaration of dynamic_cast:

    % dynamic_cast(X, Y) succeeds with Y = X iff X has the same ground type
    % as Y (so this may succeed if Y is of type list(int), say, but not if
    % Y is of type list(T)).
    %
:- pred dynamic_cast(T1::in, T2::out) is semidet.

What's a "ground type"? What does it do, except for a unification?



> > This works well with my other, non-higher order, existentially quantified
> > value. The check is accomplished by calling private_builtin.typed_unify/2.
> > And it also supplies a variable of the right type, when the types match
> > (here "Val" isn't bound yet):
> > 
> >   % The argument has a default value.
> >   MDef = yes({ Default, _ }),
> >   (   if   private_builtin.typed_unify(Default, Val0)
> >       then Val = Val0
> >       else throw(ade_type_mismatch_default(ArgDesc,
> >                type_desc.type_of(Default), type_desc.type_of(Val)))
> >   )
> > 
> > > > I want to do it inside a predicate that has a universally
> > > > quantified type variable T.
> > > > 
> > > > I have done something similar with a non-higher order type, and was able to
> > > > extract the value with private_builtin.typed_unify/2. (I've taken this from
> > > > the univ library.) This works.
> > > > 
> > > > But you can't unify two higher-order terms, so can the same be achieved
> > > > somehow for a higher-order type?
> > > 
> > > I'm guessing a bit what you're doing here, but the usage you're
> > > looking for is the one for univ_value, not univ_to_type. Here's the
> > > relevant signatures:
> > > 
> > > :- pred univ_to_type(univ, T).
> > > 
> > > :- some [T] func univ_value(univ) = T.
> > 
> > univ_to_value just does a deconstruction of the univ_cons/1 data
> > constructor. That's what I've done with "checker", see line 1370, above, but
> > it doesn't fit my case.
> > 
> > > The question is, do you want the caller to determine the type, or the
> > > callee? For univ_to_type, the caller determines the type and the
> > > implementation has to do a dynamic type check to make sure it matches
> > > the type stored in the univ - that's what typed_unify does.
> > 
> > I want the caller to call the callee only when its own polymorphic type T
> > matches the type of the callee (see above). This means I need something like
> > typed_unify/2.
> > 
> > You could say that both determine the type. This can't be done with
> > universally or existentially qualified types alone. You need runtime type
> > information and something like typed_unify.
> > 
> > > For
> > > univ_value, the callee determines the type and the caller would have
> > > to accept whatever type it was given (by only calling polymorphic
> > > things).
> > > 
> > > In the print_univ example, the last else branch could be written as:
> > > 
> > >     io.format("no idea... %s\n", [s(string(univ_value(U)))], !IO)
> > > 
> > > The other branches deal with specific types, but this one can deal
> > > with any type - univ_value/1 returns a value of _some_ type that is
> > > only known at run time, but that is okay because string/1 can accept
> > > values of _all_ types at run time.
> > 
> > Yes, I'm aware of this.

Cheers,
Volker
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: This is a digitally signed message part
URL: <http://lists.mercurylang.org/archives/users/attachments/20221011/55ea75fb/attachment.sig>


More information about the users mailing list