[Ur] Polymorphic variants and JSON

Adam Chlipala adamc at impredicative.com
Sat Apr 14 17:29:39 EDT 2012


Edward Z. Yang wrote:
>      1. I can't tell what arguments are implicit and which are explicit.
>         My first intuition is that things with triple colons are implicit,
>         but it turns out Urweb can infer a bit more than I thought, and
>         I have a really hard time judging it.

Without any explicit overriding of implict-ness, the only implicit 
arguments are (a) constructor (type-level) arguments with [:::] in their 
type portions, (b) type class witnesses, and (c) folders.

> For example, I think I
>         want to use Variant.destrR. Which arguments do I need to pass
>         it, which do I not?  The rampant use of un-inferred application (@)
>         makes that harder.

I _think_ the above explanation (which is also lurking somewhere in the 
manual) says exactly which arguments are required.  Note (also as 
explained in the manual) that [@] switches to explicit passing of type 
class witnesses and folders, but leaves constructor (i.e., type-level) 
arguments implicit.  You use [@@] to make all three categories explicit.

>      2. As a relative novice to metaprogramming in general, my understanding is
>         definitions tend to be hard to read and write, but easy to use (this is in
>         contrast to value level computation, where looking at the body tells me
>         basically what a function does.)  But there are no docs, usage or
>         otherwise, for most of the combinators in the metaprogramming libraries.
>         This impedes understanding.

To me, the types more or less explain what the functions must do, thanks 
to "theorems for free"!  And then the function names add a bit more of a 
nudge, in cases with some ambiguity.

Do you have a question about a particular function that I might be able 
to explain?

>      3. Each level of the type system (kinds, types, values) has its own
>         syntax, and while this is not necessarily a bad thing it makes
>         it a bit more complicated to assess what is what, being unfamiliar
>         with the syntax.  The fact that the grammar given in the manual
>         uses Unicode symbols and not ASCII symbols exacerbates the problem.
>         (Do we have an ASCII grammar lying aruond somewhere?)

The closest thing to an ASCII grammar right now is src/urweb.lex and 
src/urweb.grm, unfortunately.  The list of ASCII-to-LaTeX mappings is 
pretty small, so I hope it doesn't take long to internalize, and then 
the grammar in the manual should be straightforward to interpret.

> Now, for the particular problem I'm trying to figure out: I'm writing the
> ToJson method for the json_variant typeclass:
>
>      fun json_variant [ts ::: {Type}] (fl : folder ts) (jss : $(map json ts)) (names : $(map (fn _ =>  string) ts)) : json (variant ts) =
>
> I am given a value of type (variant ts).  This variant has a string name associated
> with it in names, and the inside json typeclass to call for the inner data in jss.
> So what I would like to do is use variant to "destruct" jss and names, gaining
> access to a string (the name of the variant) and the appropriate typeclass
> object for the inner data, and access to the inside data of the
> variant (with these two types unified appropriately.)
>
> OK. What do I want? How do I start? Do I want destructR? It doesn't seem
> to have quite the right signature.

All of the specific functions like those in [Variant] are defined in the 
end in terms of [fold], which comes from [Top], a module that is open by 
default in all Ur programs.  I think it's a nice down-the-rabbit-hole 
exercise to understand how to build up the tower of helpful functions, 
starting just from [fold]; but of course you will want to avoid that 
when possible. :)

It looks to me like, for the [ToJson] part, you would ideally use a 
close variation of [Variant.destrR].  In particular, you want a function 
that takes one variant and two records, instead of one variant and one 
record.  Does it make sense that this is the key missing piece?  Do you 
see how to implement it?  I think it can be done without any new folding 
by combining [Variant.destrR] and [map2] (from [Top], though you don't 
need to qualify it as such to use it).

For the [FromJson] direction, I don't see a direct match with an 
existing fold function.  Intuitively, you want to fold over [jss] and 
[names], building a function that checks if the current name is in the 
JSON record syntax, and, if so, uses the current element of [jss] to 
parse the data.  (This is all modulo character-level decomposition of 
the string, which can be done as demonstrated in [json_record], though 
it may be easier for variant encodings of fixed JSON record size.)  The 
folding over two records part can be done with [foldR2] from [Top], but 
I don't see a way to avoid some manual manipulation of variants without 
writing new code.  (Though I can see [Variant.weaken] being useful to 
support a simple choice of accumulator type in the fold.)

I believe knowledge of how to accomplish the above tasks will follow 
from an understanding of the types of the different fold-based 
functions, starting with the most basic [fold], so please fire away with 
questions about them!



More information about the Ur mailing list