[m-rev.] for review: convert parse trees to strings

Zoltan Somogyi zoltan.somogyi at runbox.com
Wed Nov 1 19:12:43 AEDT 2023


On 2023-11-01 17:59 +11:00 AEDT, "Julien Fischer" <jfischer at opturion.com> wrote:
>> What prompted my question was the arrangement I have in mercury_json,
>> where most of the work is subcontracted to (private) submodules.  That's
>> not uncommon -- the examples in the compiler and term_io are just not
>> large enough to warrent it. My suggestion would be that at minimum, the
>> optimisation should apply to a module plus its child modules.
> 
> All that said, the situation in mercury_json is more general than what
> your proposal covers, in that it has:
> 
> 1. Multiple class constraints on most of the predicates that would need
>    to be specialised, typically these
> 
>    stream.line_oriented(Stream, State),
>    stream.unboxed_reader(Stream, char, State, Error),
>    stream.putback(Stream, char, State, Error)
> 
> 2. Some of the type variables are shared between constraints.
> 
> 3. Some of the constraint arguments are ground.

Your example exihibits 1 and 2, but not 3. What do you propose to do
about/with ground args?

> Multiple constraints could be dealt with by making the first argument
> a list, e.g.
> 
>   :- pragma type_spec_constrained_preds(
>        [line_oriented(Stream, State),
>         unboxed_reader(Stream, Unit, State, Error)
>         putback(Stream, Unit, State, Error)],
>        [Stream = io.text_output_stream, Unit = char,
>         State = io.state, Error = io.error]).
> 
> (Indeed, by making the second arg. a list of of lists of type variable
> asssigments, a user could request multiple specialisations in a single
> pragma.)

Ok. I see two issues there, one syntactic, one semantic.

The syntactic issue is with the second argument. Having a list of
substitions defined all at once is I think a good idea, but I wouldn't
want to use a simple list of lists to represent it all. I would much rather
use syntax such as (for the second arg only)

[subst([Stream=..., Unit=..., ...]), subst([Stream=..., Unit=..., ...])]

I think this conveys the intention of that argument significantly
more clearly than a simple list of lists.

The semantic issue concerns the first arg. It seems obvious that
for a predicate that has all the specified typeclass constraints
in that specific order with that specific arrangement of variable sharing
between them, we should apply the given specializations.
It seems intuitive that we should do the same even if the order of
the constraints on the pred/func does not match the order in the pragma.
But what should happen for predicates whose typeclass constraints specify

- a nonempty but strict subset of the constraints in the first arg, and/or
- the variable sharing arrangement that is different than in the first arg?

I have no experience with code like that, but the implementation
of the new pragma must decide what to do in each such case.
You have much more experience with typeclass-heavy code,
what do you think the semantics should be in these cases?

With my original proposal, which included only a single typeclass
constraint, the answer was fairly obvious: you apply to a pred or func
all of the type specializations mentioned in the pragmas that apply to it,
meaning you get the cartesian product if there is more than one such pragma.
I don't think that it is obvious whether that is the right answer in your use case,
where the class constraints are linked together and are NOT independent.

Zoltan.


More information about the reviews mailing list