Skip to content

UI design for inputting ADT values

Anton Trunov edited this page Aug 19, 2022 · 8 revisions

UI design for inputting ADT values

XXX: There is a tool to do it: https://github.com/Zilliqa/scilla-json-utils. Perhaps we should implement it in Scilla.

Introduction:

This document proposes a UI for easy entry of Scilla ADT values, which are to be provided as inputs (in the form serialized JSONs) to Scilla. The document assumes that all primitive typed values (IntX, String, etc) are entered as plain text.

Algebraic Data Types

ADTs in Scilla allow building composite types. Defined below is simple ADT for illustration.

type MyAdt =
  | MyConstructor1 of Int32 String
  | MyConstructor2 of ByStr20
  | MyConstructor3

An ADT is defined using one or more constructors (not to be confused with constructors from object-oriented languages). A value of type MyAdt is constructed using exactly one of these constructors. In this example, a MyAdt value constructed using MyConstructor1 will contain a tuple comprised of an Int32 value and a String value. A MyAdt value constructed using MyConstructor3 on the other hand will not contain any further values inside it.

Polymorphic ADTs

The example we saw previously defines an ADT whose composite types are fixed. A polymorphic ADT allows using type variables in place of concrete constituent types, for substitution later. Let's see an example.

type 'A MyPAdt =
  | MyPConstructor1 of 'A String
  | MyPConstructor2 of ByStr20
  | MyPConstructor3

MyPAdt is a polymorphic ADT with a type parameter (variable) 'A. When MyPAdt is, for example, instantiated with the type parameter 'A set to Int32, then MyPAdt will be the same as MyAdt. List, the Scilla built-in ADT is, for example, a polymorphic ADT. When we write List Int32, it means that we have instantiated List ADT with Int32. This in-built ADT, if it wasn't in-built, would look like

type 'A List =
  | Cons of 'A ('A List)
  | Nil

Note: At the moment, Scilla does not support user-defined polymorphic ADTs. Only the in-built ADTs List, Pair and Option are polymorphic. Users can define concrete ADTs such as MyAdt above. This limitation is however not very relevant to this article. The article will assume support polymorphic ADTs.

While definitions of ADTs can be polymorphic, when a value is constructed the ADT must already be instantiated (specialized). So all ADT values have concrete (i.e., no type variables) types. This means that ADT values that are input by a user (for example, as a transition parameter) will have a concrete type.

ADT Information

To enable tools (such as IDEs) to work with ADT values of a Scilla contract, scilla-checker, when provided the -contractinfo flag prints a detailed definition of all ADTs (including in-build ADTs) in the contract. The ADT information for List printed by scilla-checker is shown below:

    {
      "tname": "List",
      "tparams": [ "'A" ],
      "tmap": [
        { "cname": "Cons", "argtypes": [ "'A", "List ('A)" ] },
        { "cname": "Nil", "argtypes": [] }
      ]
    }

I suggest the reader to compare this information with the ADT definition of List show above and ensure that the exact same information is present in both the descriptions.

Inputting ADT Values from a UI

With a brief background on ADTs in Scilla, let's now proceed to design a UI to input ADT values from users and build a JSON from it.

Type Instantiation

The concrete type of an ADT is known prior to parsing its value. For example, MyPAdt Int32 or List Int32 represent a concrete ADT. Given the ADT description from scilla-checker, the IDE must first instantiate (specialize) this description, replacing the type variable (in this case 'A) with the concrete type (Int32 here). Unexpectedly, the number of type arguments to a polymorphic ADT must be equal to the length of the tparams field in the description JSON.

Inputting a Scilla Value

Below is a pseudo-code to input a Scilla value from the user, given its concrete type.

  Json get_input(ConcreteType t) {
    if (t is primitive type) {
       text = get_input_plain_text();
       return Json (text);
    } else {
       // Concrete ADT
       string constructors_names[];
       foreach (c in t.tmap) {
          constructor_names.append(t.tmap.cname);
       }
       constructor = get_input_from_menu(constructor_names);
       Json value;
       value["constructor"] = constructor;
       value["argtypes"] = t.tmap[constructor].argtypes;
       foreach (t' in t.tmap[constructor].argtypes) {
         value["arguments"].append(get_input(t'));
       }
    }
  }

I'm not entirely sure of the layout to present to the user, but it will, in some form, be a dynamically growing tree layout. Maybe, the simplest layout would be to present an empty skeleton JSON, and fill up the JSON, using menus for constructors and plain text fields for primitive types. The tree will grow as the user inputs more data.

The result of this algorithm, for example, for a List Int64 type will look like:

{
      "constructor": "Cons",
      "argtypes": [
        "Int64"
      ],
      "arguments": [
        "0",
        {
          "constructor": "Cons",
          "argtypes": [
            "Int64"
          ],
          "arguments": [
            "2",
            {
              "constructor": "Nil",
              "argtypes": [
                "Int64"
              ],
              "arguments": []
            }
          ]
        }
      ]
    }

This list contains [0,2]. Note: While I treat List as any other ADT here(which it is), we make an exception for Scilla List values. The elements of a List can simply be specified as a JSON array. The IDE need not allow this exception though, to keep the UI uniform and simple. Scilla allows this exception because of the possibility of hand-written JSONs.