Re: [Ur] Ur/Web first impressions

2021-03-19 Thread Joachim Breitner
Hi,

Am Donnerstag, den 18.03.2021, 19:36 -0400 schrieb Adam Chlipala:
> On 3/18/21 2:20 PM, Joachim Breitner wrote:
> > > > 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:
> > > > 
> > > >
> > > > > > >  ui <- signal ui_state;
> > > >  return  > > >drawDisplayState canvas_id (displayState ui);
> > > >return 
> > > >  }/>
> > >   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).
> I should have been more specific in my suggestion.  Instead of exposing 
> a mutable datatype of canvases, I would prefer a pure datatype of 
> functional drawing descriptions.  Then you can give your canvas a type 
> like `signal picture_description -> xbody`.  I don't see a place where 
> we'd want an operation to listen for changes to trigger mutation.


Absolutely! Such a higher-level pure API is desirable. I have been
wondering about a “canvas_drawing” monad where the only side effect is
drawing to the canvas (so it’s pure, assuming the canvas is cleared). 

There is some trickiness. Maybe a single canvas has multiple areas that
should independently react to different signals, so you either have to
be pragmatic and expect users to clear and redraw only the the relevant
parts in the various “signal (canvas_drawing unit)” handlers. Or just
don't support that, and only support “always redraw everything” flows.

So I see the point of the argument that “whenever you feel you need a
`listen` function in Ur/Web, you are probably in the process of
providing a higher-level abstraction on top of it, and so using the FFI
internal isn’t too bad, so need to put it into Basis”.

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


Re: [Ur] Ur/Web first impressions

2021-03-18 Thread Adam Chlipala

On 3/18/21 2:20 PM, Joachim Breitner wrote:

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:

   
   
 }/>

  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).
I should have been more specific in my suggestion.  Instead of exposing 
a mutable datatype of canvases, I would prefer a pure datatype of 
functional drawing descriptions.  Then you can give your canvas a type 
like `signal picture_description -> xbody`.  I don't see a place where 
we'd want an operation to listen for changes to trigger mutation.

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  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.)
The payoff from introducing an explicit data model is that you might 
find later that parts of the data you used previously should also 
influence other parts of the page, and you don't need to go back and 
change anything else -- you avoid "callback hell" of finding all the 
places a source was changed and adding new logic to modify some other 
part of the page.


___
Ur mailing list
Ur@impredicative.com
http://www.impredicative.com/cgi-bin/mailman/listinfo/ur


Re: [Ur] Ur/Web first impressions

2021-03-18 Thread Ziv Scully
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

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 
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:
> > >
> > >   
> > >> > ui <- signal ui_state;
> > > return  > >   drawDisplayState canvas_id (displayState ui);
> > >   return 
> > > }/>
> >  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  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!

Re: [Ur] Ur/Web first impressions

2021-03-18 Thread Joachim Breitner
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:
> > 
> >   
> >> ui <- signal ui_state;
> > return  >   drawDisplayState canvas_id (displayState ui);
> >   return 
> > }/>
>  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  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 ++ { 

Re: [Ur] Ur/Web first impressions

2021-03-18 Thread Adam Chlipala

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:

   
   
 }/>
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