top of page
Search

Generic Programming Applied to Business Logic Opposed to Applying it Solely to Monads

Written by Aleksandr Novikov


Generic code is very useful for implementing an algorithm on a set of types. A sorting algorithm may be applied to a list of integers or a list of strings. I hope you know how it is done. I'd like to draw your attention to something different. I'd like to present to you a way of writing business logic with generics. There is an example that is proven to be good for explaining modelling with objects. It's to describe how a car may look like in a program. Here is a generic car.

package my-project.car

case class Car[ENGINE, WHEEL](

engine: ENGINE,

wheels: (WHEEL, WHEEL, WHEEL, WHEEL)

)

Business objects we invent for modeling of the real world are very often unique. So there is no gain of massive code reuse, as the case is with more general types like integers or strings. The objects I have in mind are in a way named stages of a process that some real thing goes through before our eyes . These objects are wrappers of some state. Another state for our car might be when it gets in motion.

package imy-project.movingCar

case class MovingCar[ENGINE_ON, TURNING_WHEEL](

engineOn: ENGINE_ONE,

turningWheels: (TURNING_WHEEL, TURNING_WHEEL, TURNING_WHEEL, TURNING_WHEEL)

)

Now it's possible to alternate between the states of a car. Let's take the steering wheel in our hands and turn on the engine.

package my-project.car

trait Start[CAR, MOVING_CAR]:

def f(car: CAR): MOVING_CAR

extension(car: CAR)

def start: MOVING_CAR = f(car)

object Start:

import my-project.engine.On

import my-project.wheel.Turn

import my-project.factory.MakeMovingCar

given [ENGINE, WHEEL](using

On[ENGINE, ENGINE_ON],

Turn[WHEEL, TURNING_WHEEL],

MakeMovingCar[FACTORY, ENGINE_ON, MOVING_CAR, TURNING_WHEEL]

)(using

factory: FACTORY

): Start[Car[ENGINE, WHEEL], MOVING_CAR] with

override def f(car: Car[Car[ENGINE, WHEEL], MOVING_CAR]): MOVING_CAR =

val engineOn = engine.on

val turningWheels = (

car.wheels(0).turn,

car.wheels(1).turn,

car.wheels(2).turn,

car.wheels(3).turn

)

factory.makeMovingCar(engineOn, turningWheels)

Have you ever seen so much boilerplait code? This is an approach to coding with generic. What benefits justify such a waste of keystrokes, you might ask. The main benefit is straightforward unit-testing with stubs. To create stubs for the four wheels, one would write:

class WheelStub

val wheelStub_1 = new WeelStub

To conjure a stub for the factory, one would add:

class FactoryStub

given FactoryStub = new FactoryStub

Implementing methods for the test is easy. Let's take the engine this time.

given On[EngineStub, EngineOnStub] with

def f(engine: EngineStub): EngineOnStub =

assert(engine.equals(engineStub)

engineOnStub

No fancy mocking needed as you see.

So now you got a taste of using generic programming in business logic. If you got interested, you might want to examine a full-fledged project written in this way.

Here is an attempt to graph manipulation library for you:

Comments


bottom of page