# Cory Brown @uniqname Nav

Why yes, we are hiring. (Please come talk to me after)

# Discussion 👍

The biggest problem in the development and maintenance of large-scale software systems is complexity.

Out of the Tar Pit: Ben Moseley, Peter Marks; February 6, 2006

The major contributor to this complexity in many systems is the handling of state

Out of the Tar Pit: Ben Moseley, Peter Marks; February 6, 2006

## state

``````const sku = scan()

const item = inventory[sku]

let displayPrice = ''

if (item) {
displayPrice = item.price
}

displayPrice = displayPrice.replace('\$', '')

let price = Number(displayPrice)

let priceInCents = price * 100

let totalPriceInCents = priceInCents * TAX_RATE

totalPriceInCents = Math.floor(totalPriceInCents)

const totalPrice = totalPriceInCents / 100

const totalDisplayPrice = `\$\${totalPrice}`

``````

Every variable or method creation amounts to the introduction of state.

The more variables, the more state.

## state

``````const sku = scan()

const item = inventory[sku]

let displayPrice = ''

if (item) {
displayPrice = item.price
}

displayPrice = displayPrice.replace('\$', '')

let price = Number(displayPrice)

let priceInCents = price * 100

let totalPriceInCents = priceInCents * TAX_RATE

totalPriceInCents = Math.floor(totalPriceInCents)

const totalPrice = totalPriceInCents / 100

const totalDisplayPrice = `\$\${totalPrice}`

``````

The more variables are mutated, the greater the complexity that state introduces

## state

``````let currentState = ''
let location = ''

const app = () => {

const reinitialize = () => {
currentState = ''
}

const fetchDataForPage = (location) =>
dataFetcher(location)
.then((data) => {
currentState = data
})
.catch(() => {
currentState = 'Error!'
})

document.onPopState((locationState) => {
reinitialize()
location = locationState.path
fetchDataForPage(location)
.finally(() => render(currentState))
})
}

app()

``````

The broader the scope of the variables the greater the complexity of the state.

## state

``````document.onPopState(({ path }) =>
dataFetcher(path)
.then(render)
.catch(render)
)

``````

Conversely, The narrower the scope of the variables the less the complexity of the state.

# Mortar

When an item's barcode is scanned, the price -- including tax -- should be displayed to the customer.

## Technical requirement

Use the guid received from scanning the item to look up the price of the item from our product database.

Apply tax to the product price and display updated result.

•   scan the bar code to get a guid
•   use the guid to lookup the item information
•   get the price property from the item
•   convert the price from a string to a number
•   calculate the tax for the item
•   add the tax to the item's price
•   convert the number price to a display price
•   show the final display price to the customer

## implementation

``````const sku = await scan()

const item = await products.findById(sku)

let displayPrice = ''

if (item) {
displayPrice = item.price
}

displayPrice = displayPrice.replace('\$', '')

let price = Number(displayPrice)

let priceInCents = price * 100

let totalPriceInCents = priceInCents * TAX_RATE

totalPriceInCents = Math.floor(totalPriceInCents)

const totalPrice = totalPriceInCents / 100

const totalDisplayPrice = `\$\${totalPrice}`

``````

Perhaps it looks something like this.

# Pipelines

## pseudo code

``````  scan ->
lookup ->
extractPriceString ->
parseAsNumber ->
convertToCents ->
convertToDollars ->
convertToDisplayString
``````

## pipeline

``````pipe(
(sku) => inventory[sku],
(item) => item.price,
(displayPrice) => displayPrice.replace('\$', ''),
(Number),
(price) => price * 100,
(price) => Math.floor(price * TAX_RATE),
(totalCents) => totalCents / 100,
(total) => `\$\${total}`
)(scan())
``````

Each piece of state is scoped to one, very simple function.

No piece of state spans more than one function.

Each piece of state ceases to exist at the conclusion of its function body.

## pipeline

``````pipe(
(sku) => inventory[sku],
(item) => item.price,
(displayPrice) => displayPrice.replace('\$', ''),
(Number),
(price) => price * 100,
(price) => Math.floor(price * TAX_RATE),
(totalCents) => totalCents / 100,
(total) => `\$\${total}`
)(scan())
``````

No gap can exist from one task to another. There is no opportunity for state manipulation to happen.

In a pipeline, the flow of data is consistent and uninterrupted.

State is contained and minimized.

# The many faces of pipelines

## dot chain with arrays

``````[scan()]
.map((sku) => inventory[sku])
.map((item) => item.price)
.map((displayPrice) => Number(displayPrice.replace('\$', '')))
.map((price) => price * 100)
.map((price) => Math.floor(price * TAX_RATE))
.map((totalCents) => totalCents / 100)
.map((total) => `\$\${total}`)
``````

## dot chain with functors

``````Box(scan())
.map((sku) => inventory[sku])
.map((item) => item.price)
.map((displayPrice) => Number(displayPrice.replace('\$', '')))
.map((price) => price * 100)
.map((price) => Math.floor(price * TAX_RATE))
.map((totalCents) => totalCents / 100)
.map((total) => `\$\${total}`)
``````

## dot chain with promises

``````scan()
.then((sku) => inventory[sku])
.then((item) => item.price)
.then((displayPrice) => Number(displayPrice.replace('\$', '')))
.then((price) => price * 100)
.then((price) => Math.floor(price * TAX_RATE))
.then((totalCents) => totalCents / 100)
.then((total) => `\$\${total}`)
``````

## composition with `pipe`

``````pipe(
(sku) => inventory[sku],
(item) => item.price,
(displayPrice) => Number(displayPrice.replace('\$', '')),
(price) => price * 100,
(price) => Math.floor(price * TAX_RATE),
(totalCents) => totalCents / 100,
(total) => `\$\${total}`
)(scan())
``````

## composition with `compose`

``````compose(
(total) => `\$\${total}`,
(totalCents) => totalCents / 100,
(price) => Math.floor(price * TAX_RATE),
(price) => price * 100,
(displayPrice) => Number(displayPrice.replace('\$', '')),
(item) => item.price,
(sku) => inventory[sku],
)(scan())
``````

## composition with `pipeline operator`

``````scan()
|> await,
|> inventory.findById(sku),
|> await,
|> (({ price }) => price),
|> ((displayPrice) => Number(displayPrice.replace('\$', ''))),
|> ((price) => price * 100),
|> ((price) => Math.floor(price * TAX_RATE)),
|> ((totalCents) => totalCents / 100),
|> ((total) => `\$\${total}`)

``````

## composition with `pipeline operator`

``````scan()
|> await #,
|> inventory.getById,
|> #.price,
|> Number(#.replace('\$', '')),
|> # * 100,
|> Math.floor(# * TAX_RATE),
|> # / 100,
|> `\$\${#}`

``````

Other closely related contributors are code volume, and explicit concern with the flow of control through the system.

Out of the Tar Pit: Ben Moseley, Peter Marks; February 6, 2006

Those are topics for another discussion.

By Cory Brown

• 81