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