[m-users.] Concise syntax for initialising and updating discriminated union types

Volker Wysk post at volker-wysk.de
Tue Jul 19 04:35:26 AEST 2022


Am Dienstag, dem 19.07.2022 um 03:37 +1000 schrieb Zoltan Somogyi:
> 2022-07-19 02:50 GMT+10:00 "Volker Wysk" <post at volker-wysk.de>:
> 
> > 2. How to concisely update multiple fields? You can do this in multiple
> > steps, like this:
> > 
> > konstr1(LP3) :-
> >     LP1 = 'lp_link_target :='(null_link_parts, "Target"),
> >     LP2 = 'lp_link_subsection :='(LP1, yes("Subs.")),
> >     LP3 = 'lp_link_text :='(LP2, "bla").
> > 
> > But this too, is rather verbose and maybe error prone.
> 
> You can also do this with
> 
> LP = (((null_link_parts ^ lp_link_target := ...)
>   ^ lp_link_subsection := ...)
>   ^ lp_link_text := ...)
> 
> though matching all the parentheses can be a bit of a pain.

I see. I guess I'll do it like this, from now on.

> > The code smells very much like state variables. It could be done like this:
> > 
> > konstr3(Out) :-
> >     some [!LP] (
> >         !:LP = null_link_parts,
> >         lp_link_target("Target", !LP),
> >         lp_link_subsection(yes("Subs."), !LP),
> >         lp_link_text("bla", !LP),
> >         Out = !.LP
> >     ).
> 
> And lots of Mercury code does just such sequences of field updates,
> though none of the Mercury code I have seen started with a deliberately
> junk structure. 

I'm not happy with it either. I'd prefer to do everything with field names
of some kind.

> Amongst other things, this code will do the wrong thing
> if someone ever adds a fourth field to the structure returned by null_link_parts.

Often, it will do the right thing. But I agree it isn't good style to rely
on luck.  :-)

This problem is the reason why I speculated about some initialization
syntax, which would catch such cases, like a newly added field, which
doesn't get initialized properly.

> > 1. A concise syntax for initialising values of a discriminated union type.
> > Something like this (link_parts is a data constructor):
> > 
> > link_parts(lp_link_target = "Target",
> >            lp_link_subsection = yes("Subs."),    
> >            lp_link_text = "bla").
> > 
> > The compiler would complain when some fields aren't initialised.
> 
> This would be quite a lot of work to implement in the compiler,
> and there is already a simple way to get the effect that I presume your are
> after: a guarantee that two fields of the same type cannot be mixed up
> without that mixup being detected. 

Yes, that's what I'm after.

> That way is to use semantic wrappers
> to make fields that are semantically different be actually of different types.
> 
> In this example, this would mean defining
> 
> :- type link_target
>   --->   link_target(string).
> 
> :- type link_text
>   ---->    link_text(string).
> 
> With these, link_params(link_target("Target"), yes("Subs"), link_text("bla"))
> would work, but any permutation of the arguments would get an error.

Good point. I'm already using semantic wrappers here and there, but not
exhaustively yet.

> > 2. Some concise way to update multiple fields, like above. Maybe the
> > compiler could additionally generate field update predicates for the fields
> > (like above), similar to field update functions.
> 
> This would be easier to implement, but one would want to design
> any such new feature carefully. For example,
> 
> - how do you specify which subset of the fields you want setter predicates for
>   (in many situations, some fields should be read-only).

Couldn't this be done the same way as it's done for setter functions?

> - it is probably the usual case that you want getter predicates for all the fields, but
> 
>   -  sometimes you don't, and
>   - even if you do, you may want to export only a subset of them, leaving the others
>     private.

Dito.

> This last consideration also applies to setters, of course.
> 
> You would also want some mechanism to control the names of the generated
> predicates, though a naming scheme consisting of {get,set}_<fieldname>
> would be a good default.

You could just use the field name as the predicate name in both cases, since
they differ in the arity.

> All of this info is specific to a particular type definition, so you wouldn't want
> to specify it using a compiler option; the appropriate mechanism would seem to be
> a new kind of pragma.

Bye!

-------------- 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/20220718/fd69568c/attachment.sig>


More information about the users mailing list