[Ur] Ur/Web first impressions

Adam Chlipala adamc at csail.mit.edu
Thu Mar 18 09:51:48 EDT 2021


Thanks for all the thoughts on the language!  My responses:

On 3/17/21 6:36 PM, Joachim Breitner wrote:
> I got “Some constructor unification variables are undetermined in
> declaration” for unification variables in unused code (e.g. when I
> stopped using some temporary recursive helper function). Made me wonder
> if undetermined variables could be maybe be allowed in unused code, so
> that I don’t have to comment out that code?
Maybe, but that's not as simple of an idea as it would be in classic 
Hindley-Milner type inference.  The reason is that, in general, not all 
values of remaining unification variables are legal.  There are other 
lingering constraints, including disjointness of rows, so it really 
would involve adding a somewhat-interesting solver.
> In terms of developer usability, I was kinda expecting a “urweb watch”
> mode that, upon changes to the sources, recompiles, restarts the
> server, and maybe reloads the client. But not a big deal, I am just
> spoiled.
I sometimes use my own simple external scripting to that end (watching 
the file system for changes), for interactive demos. However, for better 
or worse, large projects will take long enough to compile that I would 
think an unpleasant user experience could follow from automatic 
recompilation.
> On the server side, the old app didn’t need any persistent state (it
> only associated state with open connections), but in Ur/Web I had to
> use the database to communicate between the open connections.
Yeah, it makes sense that that use case would require a bit of 
arbitrary-feeling setup, to use the SQL database for cross-HTTP-request 
state.  It's not at all the kind of scenario I had in mind in designing 
the language.  However, it would be easy enough to wrap all use of SQL 
in a functor exporting a pretty natural interface.
> The “serialized” type
> constructor was a bit odd; I wonder why can’t I just use the type
> directly and instead have to serialize/deserialize, but I kinda see the
> point.
I'm trying to remember why I introduced that type family years ago! Part 
of it is that I wanted to use type classes to control which types are 
allowed in SQL code values, so that functors may declare their 
expectations appropriately.  Serialization fails at compile time when 
used on inappropriate types, and that failure logic uses arbitrary 
compiler code, not a nice set of type-class instances.  So it can be 
seen as kind of a trick to get arbitrary type-level approval code into a 
type-class instance.
> 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!).

In my experience, wrapping JavaScript libraries isn't a significant 
effort bottleneck for real projects, but you've raised a number of good 
ideas for improving the FFI experience, which I would at least be glad 
to see come in as pull requests, after some discussion on this mailing list.

> But maybe the need to import JS libraries is just rare enough.
Speaking only for myself, there have been precious few JavaScript 
libraries that I've ever wanted to connect to from Ur/Web.  I think the 
tally stands at about five since 2006.  I find that most libraries used 
in web programming are making up for design mistakes in other libraries, 
Ponzi-scheme style.
> 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.
> I found it odd that I cannot have mutually recursive values of type
> transaction (which is a function of sorts), and have to add unit
> arguments. Probably a ML programmer finds that restriction natural,
> with my Haskell background it was odd.
Right: Ur/Web has eager evaluation, so it is not coherent to allow 
recursive definition of just anything!  And it's an interesting question 
how such a language can allow introduction of new classes of recursive 
things in libraries, rather than as built-in language features.  Nothing 
about transactions is built into the Ur/Web core language.
> Similarly, at one point I tried to use `s : source xbody <- source …`
> for the main views (because why introduce a data type when there is
> only a single function from that data type?). But that ran afoul a
> similar problem, as … may not refer to `s`. Again, I was probably
> thinking too Haskelly.

That example is actually easy to patch: initialize `s` with dummy code, 
then overwrite it with the new content, which may mention `s`.  (The 
only word of warning here is that the overwrite must happen in 
client-side code.)

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.

> No derivation of show, soo useful for debugging and prototyping?
> Not even for eq?
That would be great to see someone add!  You can actually get pretty 
good generic printing using conversion to JSON already, though.
> I missed a less heavy variant `let`. Especially one that lines up
> nicely with do notation (`let val x = y in e end` is a mouthful, and
> adds indentation). I resorted to `x <- return y; e`, not sure how bad
> that is.
I tried to add a lighter-weight version way back when, and I ran into 
some aggravating conflict in the parser generator.  Yes, the version 
with `return` is what I write myself these days.  I'd love to see a 
patch adding a lighter-weight version that doesn't break other notations!
> No eq for tuples predie
There is a generic implementation in UPO 
<https://github.com/urweb/upo/blob/master/record.urs>. (It's important 
to keep in mind that tuples are a special case of records in Ur/Web.)
> 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.
> 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 
<https://github.com/urweb/urweb/blob/master/src/mono_opt.sml#L186> and 
JavaScript 
<https://github.com/urweb/urweb/blob/master/lib/js/urweb.js#L3379> both 
force property names to start with letters or underscores.  Is it clear 
that hyphens should be allowed as well, with no further restrictions?
> Similarly, and just like almost any statically typed view on HTML,
> there are always tags and properties missing. In my case, tabIndex. No
> big deal, just minor friction.
I'll be happy to take a pull request adding an innocuous-sounding 
feature like that one.
> At some point I briefly used variants, but the syntax was a bit
> verbose, and they didn’t add anything of value over data types at that
> point. But later I think I could have done fun fancy generic stuff with
> them if I had them. Still makes me wonder if we can’t simply have both
> (e.g. variants everywhere).  Is it that type inference suffers too
> much?
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.
> Urweb generates data base schemas to create databases. But it seems the
> problem of migrating existing data from prior versions to the schema of
> new versions does not get any built-in help in Ur/Web. Ideally somehow
> Ur/Web allows me to specify (declaratively or by code) how to migrate
> from old versions, and I get the end-to-end static typing support that
> I came to Ur/Web for. But it’s obviously a hard problem, and discussed
> before (e.g. at https://github.com/urweb/urweb/issues/65). Still, I
> wonder how production users of Ur/Web deal with this?
I write my own migrations outside Ur/Web.  It's typically fairly easy 
with SQL schema-manipulation commands.  I also agree it would be nice to 
have a generic solution in the language tooling; all the relevant 
information is already kept, with the main complication being how to 
make it possible to talk about old versions of a schema.  That could 
actually get pretty involved, what with Ur/Web's commitment to allowing 
modules to have private tables.  IMO, it would be an antipattern to 
force consideration of an application's database schema as a monolithic, 
flat definition.  I also use apgdiff 
<https://github.com/fordfrog/apgdiff> sometimes for the dumbest possible 
automatic schema adaptation, and it usually works.
> It seems that Ur/Web projects are so nicely self-contained, they should
> be ideal candidates for serverless deployment. I guess one can’t easily
> use Amazon Lambda, because the server is actually stateful (to persist
> these client connections), but I could still imagine that some auto-
> deploy-from-github-repo-without-configuration (like netlify etc.
> offer), including data base etc. might be a nice add-on at some point,
> so that it takes just minutes from writing your foo.urp to having a
> live site.
Agreed!  I'd love to see a pull request with that kind of scripting, 
even included in the main project repo (though I believe it should be 
quite doable with minimal code from an unprivileged position).
> I imagine if I didn’t have to deal with Canvas,
I agree it's fair to point out lack of an existing library for a 
standard HTML feature was a fundamental obstacle, but...
> and maybe went for something less AJAXy and more
> classical web programming with links and POSTs,
...here I have to disagree.  I'll continue claiming that Ur/Web supports 
AJAXy programming better than anything else out there!  I don't put many 
links or old-school forms in Ur/Web applications I write these days.  
(However, many web API requests use POSTs today, so I'm not sure what 
that part of your characterization is meant to draw attention to.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.impredicative.com/pipermail/ur/attachments/20210318/8c4942b7/attachment-0001.html>


More information about the Ur mailing list