Previous ... Next

Example of a 42 program

In this chapter, we will see how to develop a GUI connected with a DataBase. For simplicity, consider that we have a database with a single table «» that contains a few fields. We want to make a GUI that displays the data and allows us to edit it.

(1/5) Query boxes

First we load the libraries we need: Unit, JavaServer, GuiBuilder and Query.

We then declare some useful units. «» objects have ages expressed in years, heights expressed in meters and weights expressed in Kgs.

We then ask «» to generate a class to support SQL queries using a Java slave and a connection string. As an example, we are using a derby DB. For now, we consider the DB to be already populated. In the end we discuss how to initialize the DB itself.

The class «» can now reify the DB tables; here it is just «».
Finally, we can make a query box with all the queries that are relevant for this application. A query box is a capability class, whose methods are able to do queries on a given database. It is obtained with the decorartor «», that can recognize nested classes created with the «» method. Since «» was created by providing the connection string, queries are aware of their database.
The symbol «» identifies parameters in the queries, while the types in «» are the query result type followed by any parameters. Queries return lists of objects. Those objects are constructed by calling a (factory) method whose arguments have the same name as the query fields.

Right now the class «» supports both «» and «». We expect to add more query languages in the future. «» is a query language to query the user by asking them to complete a form. Using «» in 42 is very similar to using «». In particular, the result of both SQL and IQL queries is a lists of objects instantiated using a unique #immK(..) method. While this is a consistent and flexible way to process tabular data, it means that for queries returning a single column we must have a type with such a single field. In 42, declaring those types and their corresponding list type takes just a single line. Note how for Person we can use our specialized units «», «» and «».

In the same way, if a query returns a single row, we will have it served as the only element of a length 1 list.

We can now make the set of all user queries with another «»:

For a complete guide to IQL, you can refer to the well designed IQL guide, located in the readme of the IQL repository.

(2/5) Model

To write a GUI we need a Model and a View. The model is an object whose methods are triggered by events of the view. Comparing this with the conventional MVC, here the model serves both the roles of the model and the controller. In this example, the model will have the two boxes and the java slave to control the Gui.

Methods annotated with «» will respond to the corresponding event from the view. Those methods must all take a single «» parameter, used by the view to communicate extra information. This parameter is often unused. Those methods are defined as follows:
«» first asks the view to clear the table; then it executes the query «» by doing «». That is: «» is the «» offering access to all the individual query objects. «» is the field access for the query object doing the «» query. Finally, since this query takes no parameters, we just use «» to call it. Calling the query returns a «» object, that is iterated with the «». We map the fields of «» onto local variables «» for easy access in the «» body. For each «», the body asks the view to add a line into the table. Information has to be encoded as a string to be passed to the view. String interpolation «» make this easy, and «» values are converted as «» to print them with decimal points instead of printing them as a fraction. Finally, we «» exceptions to assert that we do not expect them to be leaked.
This is quite a mouthful. 42 code tends to be quite compact, but for the sake of clearity and to support learning, we will now encode it again in a more verbose style:
As you can see, you are free to make the code more readable by declaring a lot of local variables, but you can also keep a more compact style. In the end, more verbose code may end up less readable simply because there is much more of it.

The other methods have a similar structure.

The method «» ask the user to provide data for a list of persons by running the «» IQL query, returning a «». Then, the data of each «» is inserted in the database. Note how the parameters of the query «» are provided using the names of the query declaration «»: «» in the query string was used to create the «» parameter; and so on for the others. Thanks to metaprogramming the query method is synthesized out of the query string.
After inserting all the new data in the database, we refresh the displayed table by manually calling «».

Methods «» and «» are very similar; they call the corresponding «» query, extract the single field (using the notations «» and «») and update the database using the corresponding «» query.

In this setting we represent persons in two different classes: «» and «». This allows for those two different classes to actually be quite different: «» have an «» field and fields «» and «» are of type «». On the other side «» have no «» field and fields «» and «» are of type «» and «». Those two classes serve different roles, and if we wish to change the kind of data the user must provid we can change «» and make it even more distant with respect to the information stored in the database. On the other side, if we wanted to apply a transformation on the data readed from the DB, we could use another custom person class, instead of «», and define and appropriate «» method to adapt the data from the database into any shape we need.

It is also interesting to consider what happens if the database schema changes. If the person table is removed, or the person fields are renamed, then we will get an error while typing the model.
In some sense we are turning events that would have caused a runtime exception into understandable compile time errors.

(3/5) View

The library «» allows to write a GUI using Java Swing. For safety reasons, Java code is compiled and executed on a separated JVM. We can easily generate a GUI for our example in the following way:

SwingUtilities.invokeLater(()->tModel.addRow(msg.split(","))));}
    |{event.registerEvent("Example.Display","tableClear",
    |  (k,id,msg)->SwingUtilities.invokeLater(()->tModel.setRowCount(0)));}
    """
  )
]]>
Where «» is a convenient way to generate a button raising 42 events. When such 42 code will run, the following Java code will be generated:
new l42Gui.L42Frame(event,"Example",800,600){
        JPanel screen1=new JPanel();
        {add(screen1);}
        JPanel buttons=new JPanel();
        {addNorth(screen1,buttons);}
        JButton addPerson = new JButton("add");{
          addPerson.addActionListener(e->event
            .submitEvent("Example","addPerson","PressedAdd"));
        }
        {addFlow(buttons,addPerson);}
        JButton removeById = new JButton("remove by id");{
          removeById.addActionListener(e->event
            .submitEvent("Example","removeById","PressedRemove"));
        }
        {addFlow(buttons,removeById);}
        JButton removeByName = new JButton("remove by name");{
          removeByName.addActionListener(e->event
            .submitEvent("Example","removeByName","PressedRemove"));
        }
        {addFlow(buttons,removeByName);}
        JButton printAll = new JButton("printAll");{
          printAll.addActionListener(e->event
            .submitEvent("Example","printAll","PressedPrint"));
        }
        {addFlow(buttons,printAll);}
        Object[] tLabels={"id","name","age","height","weight"};
        DefaultTableModel tModel=new DefaultTableModel(new Object[][]{},tLabels);
        JTable table = new JTable(tModel);
        {addCenter(screen1,new JScrollPane(table));}
        {event.registerEvent("Example.Display","tableAdd",
          (k,id,msg)->SwingUtilities.invokeLater(
            ()->tModel.addRow(msg.split(","))));
        }
        {event.registerEvent("Example.Display","tableClear",
          (k,id,msg)->SwingUtilities.invokeLater(
            ()->tModel.setRowCount(0)));
        }
      }
    );
  }
}
]]>
and in the java main «» will be called. As you can see, the code provided by the user is simply injected into the body of a «» class. From that context we can declare fields and methods, and we can declare initialization actions using (non-static) java initialization blocks. This code would look trivial if you are a Java Swing expert, and very obscure otherwise. Note how we use «» to add the row to the table: in java «» wants an array and «» will produce an array from a string whose parts are separated by «». We made in this way for the sake of a simple example, but we are unsatisfied by this brittle solution: it only works since names or numbers should not have the «» inside.

(4/5) Putting all together

Finally, a «» puts this all together

>model )//event loop
  )
]]>
As you can see, the GUI produces events on the channel «» (the name of the generated Java class) and consumes events on the channel «».

If we wanted to add functionalities to initialize and to clear the database, we could do as follow:

(5/5) Summary

42 metaprogramming allows for complex applications to be written in compact and secure ways: in this application we used «», «», «» and «». Those are all normal 42 libraries that 42 programmers could write themselves, and indeed studying the implementation of those libraries is currently the best way to become a Magrathean.

In particular, «» allows us to take queries written in another language (for now, just SQL and IQL, but the concept is expandable) and converts them into a simple 42 well typed API that can be used to build programs in an compact and elegant way. Concepts like the «» can be used to control what part of an application is allowed to do important operations, adding a great deal of security.