Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Models

Models are elements that live through the whole execution of a program. They are declared by extending an existing library model and configuring its parameters.

A simple example using an HTTP server model:

use http/server::HttpServer

model MyServer() : HttpServer {
    bind = "0.0.0.0"
    port = 8080
}

Reference for HttpServer

The MyServer model extends HttpServer from the http package, fixing the bind address and port. It will live for the entire program execution, accepting connections as long as the program runs.

Models can also expose parameters that callers can override. Here is a database connection pool with a configurable maximum:

use sql::SqlPool

model MyDatabase(const max: u32 = 5) : SqlPool
{
    min_connections = 1
    max_connections = max
    url = "postgresql://my-user@my-server:4321/my_database"
}

Reference for SqlPool

Models are instantiated by treatments in their prelude.

use sql::fetch
use sql::SqlPool
use std/data/map/block::entry
use std/data/map/block::get
use std/data/map::Map
use std/flow::trigger


treatment myApp()
  /*
    When model is instantiated, it is made available within the
    treatment as if it where given as configuration parameter.
  */
  model database: MyDatabase(max=3)
  input user_id:       Block<string>
  output user_name:    Block<Option<string>>
  output user_level:   Block<Option<u32>>
  output user_failure: Stream<string>
{
    userData[database=database]()

    Self.user_id -> userData.user_id,user_name -> Self.user_name
                    userData.user_level --------> Self.user_level
                    userData.errors ------------> Self.user_failure
}

/*
  Model can be given as configuration parameter.
*/
treatment userData[database: SqlPool]()
  input user_id:     Block<string>
  output user_name:  Block<Option<string>>
  output user_level: Block<Option<u32>>
  output errors:     Stream<string>
{

    /*
      And then passed again as configuration parameter.
    */
    fetchUser: fetch[sql_pool=database](
      sql = "SELECT name, level IN users WHERE id = ? LIMIT 1",
      bindings = ["user_id"]
    )
    entry<string>(key="user_id")
    trigger<Map>()

    Self.user_id -> entry.value,map -> fetchUser.bind,data -> trigger.stream

    getUserName: get<string>(key="name")
    getUserLevel: get<u32>(key="level")

    trigger.first --> getUserName.map,value -> Self.user_name
    trigger.first -> getUserLevel.map,value -> Self.user_level

    fetchUser.errors -> Self.errors
}

Reference for fetch, entry, get, trigger

myApp graph
myApp

userData graph
userData

In most cases, models are instantiated internally by treatments and not exposed, user developer can make direct call on model-dependent treatments without instantiating its own, just giving required parameters to the sequence. The cases where user may give its own defined model is to configure elements such as external software connections or interfaces.