[Ur] Function `queryL1` returns type `transaction (list $fs)` - How can I get the `list $fs` part and pass it to `List.mapX` - which expects type `list`?

Stefan Scott Alexander stefanscottalexx at gmail.com
Sun Jul 19 18:49:44 EDT 2015


My previous messages in this mailing list for July 2015 are mostly
obsolete, because I have revised my code.

I'm still having trouble correctly using monadic IO to:

- run an SQL SELECT transaction; and then

- use the results to build XML

- within the context of the Crud demo (which imposes certain type
requirements of its own).


Problem:

An excerpt from my code is shown below. In this scenario, the user is
editing a "child" record, using a data-bound <select> widget to select a
primary key in the "parent" table, called `status`:

  Widget = (fn [nm :: Name] =>
    let
      val statusOptionsList =
        queryL1 (SELECT Id, Nam FROM status ORDER BY Nam)
      val statusOptions =
        List.mapX
          (fn r => <xml><option>{r.Nam}</option></xml>)
          statusOptionsList  (* <<== LINE 44 IN ERR MSG BELOW! *)
    in
      <xml>
        <select{nm}>
          {statusOptions}
        </select>
      </xml>
    end)

I keep getting errors "unification failure" and "incompatible
constructors", eg:

/home/scott/wrk/urweb/crud2foreign_queryL1/crud2foreign.ur:44:14: (to
44:31) Unification failure
Expression:  statusOptionsList
  Have con:  transaction (list $<UNIF:U128::{Type}>)
  Need con: list $(([Nam = xml ([]) ([]) ([])]) ++ <UNIF:U238::{Type}>)
Incompatible constructors
Have:  transaction
Need:  list

Can anyone see the error in my 'Widget' code further above?

----

Important: My code does not occur in isolation. The result of my code
building the Widget will later be passed on to module Crud from the
existing Ur/Web demo, which has the following signature:

http://www.impredicative.com/ur/demo/crud2.html

https://github.com/StefanScott/urweb-crud2foreign-queryL1/blob/master/crud.urs

(* crud.urs *)

con colMeta = fn (db :: Type, widget :: Type) =>
                 {Nam : string,
                  Show : db -> xbody,
                  Widget : nm :: Name -> xml form [] [nm = widget],
                  WidgetPopulated : nm :: Name -> db -> xml form [] [nm =
widget],
                  Parse : widget -> db,
                  Inject : sql_injectable db}
con colsMeta = fn cols :: {(Type * Type)} => $(map colMeta cols)

val int : string -> colMeta (int, string)
val float : string -> colMeta (float, string)
val string : string -> colMeta (string, string)
val bool : string -> colMeta (bool, bool)

functor Make(M : sig
                 con cols :: {(Type * Type)}
                 constraint [Id] ~ cols
                 val fl : folder cols

                 table tab : ([Id = int] ++ map fst cols)

                 val title : string

                 val cols : colsMeta cols
             end) : sig
    val main : unit -> transaction page
end

So module Crud will impose some requirements on the signature of Widget.

But so far, I appear to be getting these "unification failure" and
"incompatible constructors" errors *before* passing the result on to the
Crud module.


Discussion:

My code is based on a somewhat similar example in the chat room PDF, which
runs a SQL SELECT transaction on a table (of chat rooms) to build an XML
fragment.

  http://adam.chlipala.net/papers/UrWebPOPL15/UrWebPOPL15.pdf

The chat room PDF provides an example usage of queryL1 and List.mapX (in
figure 2, at the top of page 4):

Also on page 4, the chat room PDF provides the following descriptions of
functions queryL1 and List.mapX:

  queryL1 - Return as a list (justifying the L) the results of a query that
only returns columns of one table (justifying the 1).

  List.mapX - Apply an XML-producing function to each element of a list,
then concatenate together the resulting XML fragments to compute the result
of mapX.

Note (1): The above description of queryL1 says that it returns a "list".
It does not say that it returns a "transaction" or a "transaction list" -
but this may of course be due to an informal use of language here.

Note (2): The invocation of queryL1 and List.mapX, and the way the result
produced by queryL1 is "consumed" by List.mapX, are different in the chat
room PDF versus in my program, eg:

(a) The chat room PDF uses `<-` notation (see the first line of function
main);

(b) The result of calling List.mapX in the chat room PDF is consumed by
`main() : unit -> transaction page`;

(c) In my code, the result must be appropriate for signature of Widget
declared in the Crud module.


Conjectures:

There must be something wrong with my syntax attempting to use monadic IO
to get the result of the SQL transaction and use it to build the XML
fragment, and/or with the overall "flow" of function calls I'm using to run
the SQL and build the XML expression.

Furthermore, I'm not even sure if it's possible to put the result of a
*transaction* into the place where I want to put it: in the definition of
function Widget, whose signature is already declared in crud.urs as:

Widget : nm :: Name -> xml form [] [nm = widget]

Maybe I could phrase my question some other way: Can I even *use* functions
such as queryL1 or queryX1 to define a function satisfying the signature of
Widget above?

Those functions return type `transaction` but Widget expects something of
type `xml` so maybe I'm going about this all wrong??

But then again, I do expect that I should be able to somehow "unbox" the
result of a transaction, like any other monad. I don't know if there is a
trivial pattern-matching syntax commonly used for this (as with using a
case statement on Some or None in the Option monad) - or perhaps its part
of the notation involving `;` and `<-` and `return`.


Motivation:

I'm attempting to construct a data-bound <select> widget to let the user
easily edit a foreign-key field in a "child" record (where the options in
the <select> element would display a list of <option> elements, each
containing a value of the "parent" table's primary key).

This kind of data-bound foreign-key <select> widget is essential for
writing practical web applications using Ur/Web.


Remark:

I apologize for using Ur/Web as my language for starting to try to use
monads, rather than Haskell or ML (although I have tried to read as much as
possible on monads in Haskell and ML for the past year, as a way to prepare
to use Ur/Web).

I think seeing an example of how to run SQL to generate XML to build this
sort of data-bound <select> widget in Ur/Web (in the context of the
existing Crud demo) would be the most motivational example to really
clarify the Transaction monad to me.

As Adam has remarked, I am a monadic newbie - in the sense that I still
don't grasp the syntax to pass around and access values in a monad.

On the other hand, I have been trying to learn this stuff - studying
Haskell and ML and Ur/Web.

The fact that I can formulate the question as follows...

  Function `queryL1` returns type `transaction (list $fs)` - How can I get
the `list $fs` part and pass it to `List.mapX` - which expects type `list`?

...Is that good or bad? Does it mean I'm starting to grasp it - or am I
still totally lost?


Complete source:

The complete example is on github, here:

https://github.com/StefanScott/urweb-crud2foreign-queryL1


Complete error message:

The complete compiler error message is Issue 1 on the same github repo:

https://github.com/StefanScott/urweb-crud2foreign-queryL1/issues/1

Thanks!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.impredicative.com/pipermail/ur/attachments/20150719/3b6d6ae1/attachment-0001.html>


More information about the Ur mailing list