---
title: STRUCT Model Template
author: 
  name: Dr Gavin Rhys Lloyd
  affiliation: Phenome Centre Birmingham, University of Birmingham, UK
output:
  BiocStyle::html_document:
    toc_float: true
  BiocStyle::pdf_document: default
vignette: >
    %\VignetteIndexEntry{STRUCT Model Template}
    %\VignetteEngine{knitr::rmarkdown}
    %\VignetteEncoding{UTF-8}
---

```{r,include=FALSE,purl=FALSE}
library("struct")
```

# Using helper functions

STRUCT provides a number of helper functions that can be used to create a new struct object from the command line, or in a script:

- `set_struct_obj()`: used to create a new struct object
- `set_obj_method()`: used to modify a method for a struct object
- `set_obj_show()`: used to modify the displayed output for a struct object

## Creating a new struct object

To create a new struct object use the `set_struct_obj()` function. There are several inputs which are described below.

- `class_name`    The name of the new struct object (character).
- `struct_obj`    The type of struct object to create e.g. model, iterator, metric, etc.
- `stato`         TRUE (default) or FALSE to add stato functionality to the object
- `params`        A named vector of types for input slots. The names will become slotnames and the types will be used to determine                     what type of assignments are allowed to the named slot (e.g. integer, character etc).
- `outputs`       As `params`, but for output slots.
- `private`       As `params` and `outputs` but for slots that are used internally by the object and not intended for use by the                      user. These slots are only accessible using \@.
- `prototype`      A named list of default values for each input/output/private slot.
- `where`         Specifies the environment in which the class definition is created. Default is `.GlobalEnv`

To demonstrate, a new model object that simply adds two inputs together will be defined.

```{r}
set_struct_obj(
  class_name = 'add_two_inputs',
  struct_obj = 'model',
  stato = FALSE,
  params=c(input_1 = 'numeric', input_2 = 'numeric'),
  outputs=c(result = 'numeric'),
  prototype=list(
    input_1 = 0, 
    input_2 = 0,
    name='Add two inputs',
    description='example class that adds two values together',
    predicted='result'
    )
  )
```

The class definition will by default be created with the name `class_name` in the `.GlobalEnv`, unless you specify otherwise using the `where` parameter. We can check that creation of the class defintion by calling `show` on the definition:

```{r}
# show the class definition
show(add_two_inputs)
```

The definition has been created but the object only has the default STRUCT methods (such as `model_apply`) assigned and so doesnt do anything yet. The default methods can be updated using other helper funtions which are described in the following sections.

## Changing the default methods

All struct objects have some default methods that are intended to be "overloaded" i.e. replaced to provide functionality specific to the new object being implemented. The helper function `set_obj_method()` provides this functionality with the following inputs:

For the example object that adds two values together, we will overload the `model_apply` method.

```{r}
set_obj_method(
  class_name = 'add_two_inputs',
  method_name = 'model_apply',
  definition = function(M,D) { # you need to supply D here even if you dont use it
    M$result = M$input_1 + M$input_2
    return(M)                  # remember to always return the input object after modifying it
  }
)
```

The default `method.apply` method for the `add_two_inputs` model has now been updated. We can check this by creating an instance of our model, setting the two input values and then calling `method.apply` on our model object.

```{r}
# create an instance of the model
M = add_two_inputs(input_1 = 3, input_2 = 5)
# use the model
M = model_apply(M,iris_DatasetExperiment())
# check the result = 8
M$result
```

We can see that the result is `8`, so the new definition for `model_apply` was used successfully.

As well as overloading default STRUCT methods (such as `model_apply`, `model_train`, `model_precict` etc) it is also helpful to overload the extend the default `show` method to make the displayed output more informative for the new object. This is described in the following sections.

## Changing the default `show` output

All STRUCT objects have a default show output, which prints the name and description for an object.

```{r}
show(add_two_inputs())
```

If you want to provide additional information when printing the new object then you can overload the show method using the `set_obj_show()` function, which has the following inputs:

- `class_name`        the name of the class to update the show method for
- `extra_string`:     a function that takes the `class_name` object as input and outputs a string

The string output from the `extra_string` function will be appended to the default `show` output_ For the example object we will print the inputs in addition to the standard output_

```{r}
# update the show method
set_obj_show(
  class_name = 'add_two_inputs',
  extra_string = function(x) {
    str= paste('This model is currently set to calculate: ',x$input_1,' + ', x$input_2,sep='')
    return(str)
  }
)

```

We can check the new show method by calling `show` on the object:

```{r}
# call the show method
show(M)
```

The additonal text `This model is currently set to calculate: 3 + 5` is included, so extending the default show method was successful.