How to add a new model
Adding a model is quite similar to
adding a new fact
with the main differences being
working within the Models directory and
the creation of the model’s Haskell module
in Model.hs.
-
Add a new directory for your fact in
fact-models/src/Models. UseCamelCasefor the model’s name. -
In this directory, create a
Model.dhallfile in which to define your new model, as describe in anatomy of Model.dhall. -
To check the new model, run the following command from the same directory as the new
Model.dhallfile.cabal run fact-models:exe:fact -- build --model=NameOfModelThis command also creates the files derived from
Model.dhallsuch as:Model,Model.dhallType, etc. -
Add entries for the new model in:
-
fact-models/src/Models/package.dhall -
fact-models/src/package.dhallin themodel-srcsobject -
docs/modules/models/pages/models.adoc
-
-
Implement the model in Haskell. Follow instructions in the section below.
Anatomy of Model.dhall file
See
anatomy of a fact
for a complete description of the 4 key elements of a Model.dhall file:
-
description
-
Type(see note below) -
predicate constructor
Type -
examples
The Type for a model should be union type whose variants should be
constructors of
facts.
In the example below, the following lines imports the facts package
which can be used to define variants within the model type:
let facts = ../../Facts/package.dhall
let ExampleModel =
< FooPlan : facts.plan.Type
| Bar
| BazValue : facts.tn_value.Type
>
Complete example
{- |
{- tag::description[] -}
= ExampleModel
An example data model.
{- end::description[] -}
-}
let Model = ../../utils/Model.dhall
let facts = ../../Facts/package.dhall
let ExampleModel =
< FooPlan : facts.plan.Type
| Bar
| BazValue : facts.tn_value.Type
>
let exampleFact1
: ExampleModel
= ExampleModel.FooPlan facts.plan.examples.head
in Model
ExampleModel
exampleFact1
([] : List ExampleModel )
Creating the model’s Haskell module
-
Add
Model.hsin same directory asModel.dhall. A complete example of this file for theExampleModelis shown below and should be used as a template for creating a new model. See the API docs (at this time this means reading the source files) for theconstruct*andderive*functions infact-models/src/Utilties.hs. for more detailed instructions. -
In the
cabalobject infact-models/src/package.dhall, add an entry forModels.MyNewModelin thelibrary.exposed-modulesfield. -
Run
nix run .#build-fact-models-manifeststo updatefact-models.cabal. -
Now run
cabal build fact-modelsto check that the new Haskell module successfully builds. -
Add a
Tests.hsmodule for the Model to add tests for the model. See an existing model’sTest.hsfile for an example. Add tests model tofact-modelstest suite infact-models/src/Tests.hs. -
Run
cabal test all --test-show-details=alwaysto be sure your tests run and succeed.
Complete example
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Models.ExampleModel.Model where
import Data.Aeson
import Data.Generics.Internal.VL.Lens (view, (^.))
import qualified Data.Generics.Product as L (HasField (field), getField)
import Dhall.TH
import FactModel
Dhall.TH.makeHaskellTypes
( constructEnumFact "PlanExchange"
<> constructFact "Plan"
<> constructSumFact "TNValue"
<> constructModel "ExampleModel"
)
------ Fact class instances
deriveFactClasses ''PlanExchange
deriving instance Ord PlanExchange
deriveFactClasses ''TNValue
deriveFactClasses ''Plan
------- Model class instances
deriveFactClasses ''ExampleModel