kode4food
Fate Programming

Updated 7 months ago

A Fate Introduction

Fate is a programming language that targets the V8 JavaScript VM. It is a mostly functional language that provides first-class patterns, invocation guards, list comprehensions, flexible function application, and awesome concurrency.

The design of Fate is guided by one principle:

Data in a distributed system cannot be tamed

I started designing Fate because I was developing highly decoupled systems. These systems consumed JSON as their messaging payload, which provides great flexibility in implementation. But while JSON is ideal for transport and schema evolution, it can be difficult to process.

Fate attempts to address some of this difficulty by performing run-time dynamic checks on data via Patterns. These checks influence the way Fate programs process data. They also allow for clean code without excessive branching. (At least I think so)

Note: This guide is a work in progress.

A Concrete Example

Let's say you needed to calculate the NOx emissions for an OBD II reporting module. You could do it the obvious way:

def calculateVehicleEmissions(car)
  if car.wheelsInMotion > 2
    car.emissions
  else
    car.emissions / 40
  end
end

But that's a lot of if statements. Yes, one is too many. It also packs calculations for two different potential states into a single function. They will become more difficult to isolate should you need to hide the second calculation from government auditors. To correct this, you can break the function up and use a guard on its re-opened version.

def calculateVehicleEmissions(car)
  car.emissions
end

def calculateVehicleEmissions(car) where car.wheelsInMotion <= 2
  car.emissions / 40
end

That's better! Now if the EPA come to your place of business, you can simply delete the second function and they'll be none the wiser! On second though, that where clause is practically like another if statement. We've already established that we don't like those, so let's use an in-line pattern instead.

def calculateVehicleEmissions(car)
  car.emissions
end

def calculateVehicleEmissions({ .wheelsInMotion <= 2 } as car)
  car.emissions / 40
end

Better! But now you have the pattern matching for qualifying cars in a place where it can't be reused. Let's clean that up:

def calculateVehicleEmissions(car)
  car.emissions
end

let VehicleUnderTest = ~{ .wheelsInMotion <= 2 }

def calculateVehicleEmissions(VehicleUnderTest as car)
  car.emissions / 40
end

Done! The problem of vehicle emissions testing is now solved!

Important to understand is that VehicleUnderTest is not a type declaration to be used during compilation. It is a Pattern, which is compiled into a Function. It is then executed as a predicate when your Function is invoked. If the Pattern doesn't match, the call falls through to the previous declaration, and so on. This is the Fate value proposition.

Getting Started

You'll need to install a version of Node.js greater than 4.0 before you can get started. The reason for this is that Fate's code generator targets ES6 features that older versions of Node won't support out of the box.

Assuming that you've installed Node, you can now install the Fate compiler as a global npm package.

npm install -g fatejs

This will link two command line executables into your PATH. The first is fate. It is the command-line interpreter, and allows you to start Fate scripts directly from the command-line.

fate my_script.fate

fatec is the command-line compiler, and allows you to convert Fate scripts into node.js modules. Those modules can then be required like any other node module. We'll discuss this tool in more depth later. For now, all you need to proceed is the fate interpreter.