con ui widget value = {Render : widget -> xbody,
                       Value : widget -> signal value}

fun mkUi [widget] [value] (x : ui widget value) = x

fun render [widget] [value] (u : ui widget value) (x : widget) = u.Render x
fun value [widget] [value] (u : ui widget value) (x : widget) = u.Value x

con input _ = source string

fun ui_input [t] (_ : read t) =
    mkUi {Render = fn s => <xml><ctextbox source={s}/></xml>,
          Value = fn s => v <- signal s; return (read v)}

val input [t] = source ""

con select t = {Options : list t,
                Source : source string}

fun ui_select [t] (_ : show t) =
    mkUi {Render = fn s => <xml><cselect source={s.Source}>
                             {List.mapXi (fn i x => <xml><coption value={show i}>{[x]}</coption></xml>) s.Options}
                           </cselect></xml>,
          Value = fn s => n <- signal s.Source;
                     case List.nth s.Options (readError n) of
                         None => error <xml>Invalid option</xml>
                       | Some x => return x}

fun select [t] (options : list t) =
    s <- source "0";
    return {Options = options,
            Source = s}