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
. UseCamelCase
for the model’s name. -
In this directory, create a
Model.dhall
file 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.dhall
file.cabal run fact-models:exe:fact -- build --model=NameOfModel
This command also creates the files derived from
Model.dhall
such as:Model
,Model.dhallType
, etc. -
Add entries for the new model in:
-
fact-models/src/Models/package.dhall
-
fact-models/src/package.dhall
in themodel-srcs
object -
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.hs
in same directory asModel.dhall
. A complete example of this file for theExampleModel
is 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
cabal
object infact-models/src/package.dhall
, add an entry forModels.MyNewModel
in thelibrary.exposed-modules
field. -
Run
nix run .#build-fact-models-manifests
to updatefact-models.cabal
. -
Now run
cabal build fact-models
to check that the new Haskell module successfully builds. -
Add a
Tests.hs
module for the Model to add tests for the model. See an existing model’sTest.hs
file for an example. Add tests model tofact-models
test suite infact-models/src/Tests.hs
. -
Run
cabal test all --test-show-details=always
to 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