This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Paradigms

ready4 software is implemented using a combination of object-oriented and functional programming paradigms.

1 - Why ready4 is object oriented

ready4 uses an object oriented programming (OOP) paradigm to implement computational models.

This below section renders a vignette article from the ready4 library. You can use the following links to:

Motivation

The practical utility and ease of use of computational health economic models are in part shaped by the choice of programming paradigm used to develop them. ready4 adopts an object oriented programming (OOP) paradigm which in practice means that the framework principally consists of classes (representations of data structures useful for modelling health systems) and methods (algorithms that can be applied to these data-structures to generate insight useful for policy-making). Adopting an OOP approach is particular useful for helping to make models transparent, reusable and updatable.

Implementation

Modular Computational Models

Two commonly noted features of OOP - encapsulation and inheritance are particularly useful when developing modular computational health economic models.

Encapsulation

Encapsulation allows us to define the data structures (“classes”) used in computational modelling projects in a manner that allows them to be safely combined. For example, assume there are two computational health economic models, one (A) focused on predicting the types and intensity of services used by individuals that present to health services and the other (B) that predicts outcomes for recipients of these services. It may be desirable to develop a new model (C) that combines A and B to model both service use and outcomes. Using encapsulated code allows all of the features and functionality of A to be made available to B in a manner that protects the integrity of A. Specifically, B can only interact with A using the algorithms (“methods”) that have been already defined for A.

Furthermore, if appropriately implemented, methods associated with a class will work with any combination of input values that can be encapsulated by that class - making computational models more transferable. For example, imagine a class (X) that is used to structure summary data relevant to health systems. Methods associated with X (e.g. a method to derive an unmet need statistic) can then applied to two instances of X - one containing data relevant to the Australian context and one with data from the UK context.

Inheritence

The examples highlighted in the previous section have some potential limitations. What if the developers of A didn’t define methods that would allow B to interact with it in the desired way? Or what if there are a number of differences between the Australian and UK system that need to be accounted for when transferring a method from the former to the latter? These types of issues can be addressed by another key feature of OOP - inheritance. Inheritance allows for a “child” class to be created from a “parent” class. By default, the “child” inherits all of the features of the “parent” including all methods associated with the “parent” class. Importantly however, alternative or additional features can also be specified for the “child” to allow it to implement different methods where necessary. For example, when developing our new computational model C we could create a number of new classes that are children of the classes defined in A. We can then define any additional/alternative methods for these classes that overcome any integration issues between the classes and methods of A and B. In this way, we can enjoy the best of both worlds - leveraging all relevant algorithms from A and B (as there is no need to re-invent the wheel), while ensuring that we transparently develop the additional code required for C. This approach also ensures that the respective contributions of the (potentially different) authorship teams behind A, B and C is clearer.

Similarly, inheritance would allow re-use of much of the code from a model of the Australian health system when exploring similar topics within the UK context, while making it straightforward to develop additional code that addresses relevant divergence in features between the two jurisdictions. In practical terms, this would mean developing two child classes of X - class Y for use with Australian data and class Z for use in the UK system. All methods that are not specific to a particular jurisdiction are defined for X and inherited by Y and Z. Methods that are only appropriate for use in the Australian context are defined for Y, while UK specific methods are defined for Z.

Transparent Computational Models

To make modelling analysis programs more readily understood, the ready4 package provides a simple and consistent syntax. Such simplified approaches are facilitated by two other commonly noted features of OOP - polymorphism and abstraction.

Polymorphism

Polymorphism allows for similar concepts to be represented using consistent syntax. The same top level code can therefore be generalised to multiple model implementations, making algorithms simpler to understand and easier to re-use.

Returning to a previous example, the exact same command (e.g. a call to the method exhibit) can be applied to both Y (used for Australian data) and Z (used for UK data). However, the algorithm implemented by that command can vary based on the class that each method is applied to (ie a different algorithm is applied when the data is specified as being from the UK compared to being specified as Australian).

Abstraction

The simplicity enabled by polymorphism is enhanced by Abstraction, which basically means that only the briefest and easiest to comprehend parts of the code are exposed by default to potential users. Once an instance of a model module template class is created, the entire program to ingest model data, analyse it and produce a scientific summary can be represented in a few brief lines of code, readily comprehensible to non-coders. When using open source languages, the elegance and simplicity of abstraction does not restrict the ability of more technically minded users exploring the detailed workings of the underpinning code.

2 - The role of functional programming in ready4 development

ready4 uses functional programming to maximise the re-usability of model algorithms.

Although the object-oriented programming (OOP) approach ready4 implements has many advantages, it can also have some limitations. Some of these limitations have been colorfully highlighted by a popular quote attributed to Joe Armstrong:

“The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.”

In practical terms, this means that if not carefully planned, using OOP can create barriers to code-reuse as algorithms come bundled with artefacts of no/low relevance to many potential users. To help maximise the accessibility and re-usability of ready4 algorithms, these algorithms are primarily written using the functional programming paradigm. Only once an algorithm has been implemented using functions are they then linked to a data-structure by means of a calling method. The typical development workflow for a ready4 computational modelling project might therefore look something the following three step process:

  1. A modelling study algorithm is initially implemented as a program.

  2. To help transfer the methods used in the study algorithm, it is decomposed into functions, which are bundled as a code library (or libraries). The program is updated to use the newly authored functions.

  3. A model module is authored from a template to define a data-structure. A method (or methods) that call the functions authored in the previous step is attached to the module(s) using the ready4 framework’s syntax. The new module is added to the previously created code library and the program is again updated so that the algorithm is now implemented by supplying data to the ready4 module and then calling the desired method(s).

Modellers using ready4 for the most part will only use ready4 modules and will rarely interact directly with the functions that implement module methods. However, these functions are potentially of significant usefulness to coders authoring new algorithms. All ready4 functions are created with minimal, but consistent documentation with the aid of tools from the ready4fun library.