Example reports

In this section, you can see a number of report examples using the value expression language.

Running these examples requires that a contract has been instantiated with the example report declaration included.

Events filtering

In this section, we assume that a number of events of different types are bound in scope with the name events.

Filter events by type

To extract events of a certain type, the List::mapMaybe function from the standard library is very useful. This function will take a list as input and return a list with only the elements matching the predicate.

type Sale: Event {}

// Returns "Some e" if the event is a Sale. "None" otherwise
val isSale = \(e: Event) -> type x = e of {
        Sale -> Some e;
        _ -> None
    }

// Holds only events of type "Sale"
val saleEvents = \contractId ->
  List::mapMaybe isSale (getEvents contractId)

Filter events by property

To extract events with a certain property, we again use the List::mapMaybe function from the standard library. Here, only sales worth more than 10.0 Euro is returned.

type Sale: Event { amount: Float }

// Returns "Some e" if the event is a Sale worth more than `v`. "None" otherwise
val isSaleOverV = \v -> \(e: Event) ->
    type x = e of {
        Sale -> if (x.amount >= v) Some e else None;
        _ -> None
    }

// Holds only events of type "Sale" with amount over 10.0
val saleEventsOver10 = \contractId ->
  List::mapMaybe (isSaleOverV 10.0) (getEvents contractId)

Sum event field for event type

To extend the previous example, we not only extract the relevant field from the event, but also use that field to create a sum of the amounts. Furthermore, instead of the past events from a single contract, we take as input a number of contract id’s for which to sum the fields. Generally speaking, we apply the sum function using a flatmap.

type Sale: Event {amount: Float}

// Applies a `sum` function over a list of Floats
// sumFloat :: List Float -> Float
val sumFloat = \list -> foldl (\(acc:Float) -> \x -> acc + x) 0.0 list

// Applies the aggregation function `f` on a list of lists
// flatmap :: (List a -> a) -> List a -> a
val flatmap = \f -> \ls -> f (List::map f ls)

// We extract the `amount` field from all `Sale` events
// for contracts specified in `cids` where the `agent`
// is `customer`
val inp = \cids -> \customer -> let
        val predicate = \(event1:Event) -> 
            type event = event1 of {
                Sale -> if(event.agent = customer) Some event.amount else None;
                _-> None 
            }
        val listByEventtype = \contractID ->
            List::mapMaybe predicate (getEvents contractID)
    in List::map listByEventtype cids

val total = \cids -> \customer -> flatmap sumFloat (inp cids customer)

Custom data structures

In the following we present some standard data structures and show how they can be implemented in CSL. As these structures are not built into the language itself we must define them ourselves if we need a structure that satisfies certain properties.

Set

A Set is a collection data structure that guarantees that all entries are unique.

module Set {
  val empty = []
  val contains = \(k:String) -> List::any (\e -> e = k)
  val remove = \(k:String) -> List::filter (\x -> not (x = k))
  val insert = \(k:String) -> \(l:List String) -> Cons k (Set::remove k l) 
  val toSet = foldr Set::insert []
  val isEmpty = \l -> List::length l = 0
}

val set1 = Set::insert "one" Set::empty
  // ["one"]

val set2 = Set::insert "one" (Set::toSet ["one", "two"])
  // ["one", "two"]

val set3 = Set::contains "two" (Set::toSet ["one", "two"])
  // True

val set4 = Set::remove "one" (Set::toSet ["one", "two"])
  // ["two"]

val set5 = Set::remove "foo" (Set::toSet ["one", "two"])
  // ["one", "two"]

Association list

An association list is a map-like data structure where keys and values are pairs.

module AssocList {
  val empty = []
  val contains = \ (k:String) -> List::any (\(x, _) -> k = x)
  val remove = \ (k:String) -> List::filter (\(x, _) -> not (x  = k))
  val update = \ (k:String) -> \ v -> \ l -> Cons (k, v) (AssocList::remove k l)
  val keys = List::map fst
  val values = List::map snd
  val isEmpty = \l -> List::length l = 0
}

val c = AssocList::update "a" 4 [("a", 1), ("b", 2)]

val vs = AssocList::values c
  // [4, 2]

val ks = AssocList::keys c
  // ["a", "b"]

Complex reports

Combining some of the bits and pieces from previous examples, we are able to build more complex report functions.

Income statement

The income statement is a financial document that indicates how the revenue is transformed into net income over a period of time. In this example, we create a very simple report for computing the income statement based on Sale and Purchase events and their amounts.

// Event type stubs for sale and purchase
type Sale: Event { amount: Float }
type Purchase: Event { amount: Float }

// Relevant contract IDs. I. e. all contracts for an Agent
// contractIds : List ContractId
val contractIds = [
                     // Input all contract IDs
                  ]

val fromDate = #2016-01-01T00:00:00Z#
val toDate = #2016-12-31T23:59:59Z#

// Applies a `sum` function over a list of Floats
// sumFloat :: List Float -> Float
val sumFloat = \list -> foldl (\(acc:Float) -> \x -> acc + x) 0.0 list

// Applies the aggregation function `f` on a list of lists
// flatmap :: (List a -> a) -> List a -> a
val flatmap = \f -> \ls -> f (List::map f ls)

val dateFilter = \(e: Event) -> e.timestamp >= fromDate && e.timestamp <= toDate

val income = \myself -> let
  /// Expenses
  ///
  val isMyPurchase = \(e: Event) -> 
    type x = e of {
      Purchase -> if (e.agent = myself && dateFilter e) Some x else None;
      _ -> None
    }

  // Holds all purchase events for all contracts as a list
  val purchaseEvents =
    List::concat
      (List::map
        (\cid -> List::mapMaybe isMyPurchase (getEvents cid))
        contractIds)
  val purchaseAmount = List::map (\(s: Purchase) -> s.amount) purchaseEvents

  val expenses = sumFloat purchaseAmount

  /// Revenues
  ///
  val isMySale = \(e: Event) ->
    type x = e of {
      Sale -> if (e.agent = myself && dateFilter e) Some x else None;
      _ -> None
    }
  // Holds all sale events for all contracts as a list
  val saleEvents =
    List::concat
      (List::map
        (\cid -> List::mapMaybe isMySale (getEvents cid))
        contractIds)
  val saleAmount = List::map (\(s: Sale) -> s.amount) saleEvents

  /// Income
  ///
  val revenues = sumFloat saleAmount
in revenues - expenses