<div dir="ltr"><div dir="ltr">On the topic of whether variants would clean up your last few examples:</div><div dir="ltr"><br></div><div dir="ltr">Off the top of my head (i.e., modulo some number of compiler errors...), here's how you'd write a generic "get" function for variants and homogenous records.<div><br></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><font face="monospace">fun get [fields ::: {Unit}] </font><span style="font-family:monospace">[t ::: Type] (f : folder fields)</span></div><div><font face="monospace">        (vals : $(mapU t fields))</font></div><div><font face="monospace">        (field : variant (mapU unit fields))</font></div></blockquote><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><font face="monospace">    = match field</font></div><div><font face="monospace">            (mp [fn () => t] </font><span style="font-family:monospace">[fn () => unit -> t]</span></div></blockquote><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><span style="font-family:monospace">                (* You can maybe omit this `folder` argument. *)</span></div></blockquote><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><span style="font-family:monospace">                @(Folder.mp @@(fn () => t) @@fields @f)</span></div></blockquote><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><span style="font-family:monospace">                (fn [_ ::: Unit] (val : t) => (fn () => val))</span></div><div><span style="font-family:monospace">                vals)</span></div></blockquote><div><br></div><div>Here your <font face="monospace">color</font><font face="arial, sans-serif"> type would become </font><font face="monospace">variant [Black = unit, White = unit]</font><font face="arial, sans-serif">, and </font><font face="monospace">forColor</font><font face="arial, sans-serif"> would become a special case of </font><font face="monospace">get</font><font face="arial, sans-serif">.</font></div><div><font face="arial, sans-serif"><br></font></div><div><font face="arial, sans-serif">Writing a "set" function is a bit harder using standard-library functions. You want something like </font><font face="monospace">mp</font><font face="arial, sans-serif">, but you need access to the field name in your mapping function. You can use </font><font face="monospace">foldUR</font><font face="arial, sans-serif">, or you can write </font><a href="https://github.com/vizziv/UrLib/blob/20e4c29c88f597951dd916ed941cdcdafaef1cba/UrLib/prelude.urs#L89"><font face="arial, sans-serif">a new flavor of </font><font face="monospace">mp</font></a><font face="arial, sans-serif"> that gives access to the field names.</font></div><div><font face="arial, sans-serif"><br></font></div><div><font face="arial, sans-serif">If you decided this was all too much and wanted to write a less generic version, then you'd end up with basically a syntactically noisier version of what you already have with a color datatype.</font></div><div><br></div><div>In conclusion: this is all probably way too heavy for your use case, but as Adam mentioned, it can be very useful when metaprogramming.</div><div><br></div><div>Ziv</div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Mar 18, 2021 at 2:21 PM Joachim Breitner <<a href="mailto:mail@joachim-breitner.de">mail@joachim-breitner.de</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Hi Adam,<br>
<br>
thanks for the responses!<br>
<br>
<br>
Am Donnerstag, den 18.03.2021, 09:51 -0400 schrieb Adam Chlipala:<br>
> > Ur/Web doesn’t support canvas drawing out of the box, but I found <br>
> > <a href="https://github.com/fabriceleal/urweb-canvas/" rel="noreferrer" target="_blank">https://github.com/fabriceleal/urweb-canvas/</a>. Using a library like that<br>
> > was simple enough. Unfortunately, it didn’t cover the full Canvas API<br>
> > yet, and it seemed that adding the missing functions would require more<br>
> > boiler plate than I wanted to write right now.<br>
> <br>
> I can see how that makes sense for a quick pilot to evaluate the<br>
> language, though I expect it's pretty cost-effective to extend that<br>
> library with general Canvas features, for any kind of production app<br>
> (and then everyone else gets to benefit from the improvements!).<br>
<br>
Absolutely! And _after_ having refactored “my” JS code and learned<br>
about how to FFI-integrate it, I probably would have been more<br>
confident in just extending the library :-)<br>
<br>
> > Obviously, I need to connect the above function to a source/signal with<br>
> > the game state. I found that this works, although I wonder if this is<br>
> > an idiomatic way to do so:<br>
> > <br>
> >           <canvas id={canvas_id} align="center" width=500 height=400/><br>
> >           <dyn signal={<br>
> >             ui <- signal ui_state;<br>
> >             return <xml><active code={<br>
> >               drawDisplayState canvas_id (displayState ui);<br>
> >               return <xml></xml><br>
> >             }/></xml><br>
>  The idiomatic way would be to use the features of the FFI for adding<br>
> change listeners on signals.  You may have been thrown off by the<br>
> fact that the library you took as inspiration does no such thing! <br>
> You'll find JavaScript function "listen" documented briefly on page<br>
> 59 of the currently posted reference manual.<br>
<br>
Yes, I remembered that mention, but given that both the source and the<br>
code I want to invoke upon changes already lives in “Ur world” it felt<br>
wrong to reach for the FFI. And it was expressible with just non-FFI-<br>
features, as shown above… I guess either way is just a way to deal with<br>
the fact that I am doing something uncommon (running possibly effectful<br>
code upon changes to the signal). <br>
<br>
Given that it _is_ expressible with<br>
high-level abstractions (dyn+active) makes me wonder if there shouldn’t<br>
be a<br>
<br>
   Basis.listen : signal a -> transaction a -> transaction unit<br>
<br>
or so, with sufficient documentation about when to use it and what to<br>
keep in mind.<br>
<br>
But maybe I shouldn’t ask for exposing underlying primitives before I<br>
really needed them a few times – it could be that I’d learn more<br>
idiomatic ways and notice that they are really rarely needed.<br>
<br>
<br>
> However, I also think of the type `source xbody` as a bad code<br>
> smell.  It is usually better to put your fundamental data model in<br>
> sources and then derive HTML from them using <dyn> tags.<br>
<br>
That’s what I ended up doing in the end, and it’s probably cleaner.<br>
(Although a part of me, when it sees a data type T that is only<br>
deconstructed by a single function T -> A, wonders why not create<br>
values of type A directly. At least in a pure language.)<br>
<br>
<br>
>  That would be great to see someone add!  You can actually get pretty<br>
> good generic printing using conversion to JSON already, though.<br>
<br>
Ah, I didn’t look at the JSON stuff yet. Thanks for the hint!<br>
<br>
> > Irrefutable patterns in `p <- e; e` are useful, and I missed them also<br>
> > in `fn p => …`, especially when working with association lists.<br>
>  Actually, irrefutable patterns do work with anonymous functions! <br>
> Something else must have gone wrong to throw you off.  Maybe your<br>
> example was missing parentheses around the pattern.<br>
<br>
Indeed! I guess I didn’t try hard enough. That’s a relief :)<br>
<br>
> > The CSS parser didn’t like some property names I was using:<br>
> > “Invalid string -webkit-hyphens passed to 'property'”<br>
>  Ah, sounds like I should just change the function that validates<br>
> property names.  The implementations in SML and JavaScript both force<br>
> property names to start with letters or underscores.  Is it clear<br>
> that hyphens should be allowed as well, with no further restrictions?<br>
<br>
I hope the diagram at <br>
<a href="https://www.w3.org/TR/css-syntax-3/#ident-token-diagram" rel="noreferrer" target="_blank">https://www.w3.org/TR/css-syntax-3/#ident-token-diagram</a><br>
is the right one. Looks like it may start with a single hyphen, but<br>
that hyphen must be followed by letters or non-ASCII.<br>
<br>
>  I would like to see algebraic datatypes and polymorphic variants<br>
> unified in a complete rewrite of Ur/Web some day.  For now, variants<br>
> are really only worth using in connection with metaprogramming.  I<br>
> have almost never written code with a variant of known type.<br>
<br>
I had code like this:<br>
<br>
   datatype color = Black | White<br>
   type gameState = {<br>
     Goes_first : color,<br>
     Phase : phase,<br>
     Board : list (coord * option color),<br>
     Chosen : { Black : option int, White : option int },<br>
     Placed : { Black : option int, White : option int },<br>
   }<br>
<br>
and more such “records that are total maps from the set of colors” I<br>
wonder if using a variant for color would have allowed me to avoid<br>
helpers like the following<br>
<br>
   fun forColor r c = case c of<br>
     | White => r.White<br>
     | Black => r.Black<br>
<br>
   fun setColor r c x = case c of<br>
     | White => r -- #White ++ { White = x }<br>
     | Black => r -- #Black ++ { Black = x }<br>
<br>
or at least write them more nicely.<br>
<br>
<br>
Cheers,<br>
Joachim<br>
<br>
-- <br>
Joachim Breitner<br>
  <a href="mailto:mail@joachim-breitner.de" target="_blank">mail@joachim-breitner.de</a><br>
  <a href="http://www.joachim-breitner.de/" rel="noreferrer" target="_blank">http://www.joachim-breitner.de/</a><br>
<br>
<br>
<br>
_______________________________________________<br>
Ur mailing list<br>
<a href="mailto:Ur@impredicative.com" target="_blank">Ur@impredicative.com</a><br>
<a href="http://www.impredicative.com/cgi-bin/mailman/listinfo/ur" rel="noreferrer" target="_blank">http://www.impredicative.com/cgi-bin/mailman/listinfo/ur</a><br>
</blockquote></div>