A data language

Data deserves a language of its own

Finitio is a data definition language. Think “JSON/XML schema”, but the right way — a dedicated type system for describing data, and a theory of information contracts for moving it between programming and exchange languages.

Validating

Suppose we want to capture information about a medical diagnosis for some patient. At left, a typical digital document in JSON. At right, the corresponding Finitio schema.

diagnosis.json
{
  "patient": {
    "id": "27b3ceb0-7e10-0131-c9f1-3c07545ed162",
    "name": "Marcia Delgados",
    "dob": "1975-11-03"
  },
  "symptoms": [
    "Nausea",
    "Fever"
  ],
  "temperature": 39.5
}
diagnosis.fio
Temp = <celsius> Real( f | f >= 33.0 and f <= 45.0 )
{
  patient : {
    id   : Uuid,
    name : String( s | s.size > 0 ),
    dob  : Date( d | alive: d.year > 1890 ),
  },
  symptoms : [ String( s | s.size > 0 ) ],
  temperature : Temp
}

Suppose an invalid document comes in. With Finitio, you properly validate input data and get error messages that say exactly what went wrong, and where.

invalid.jsonrejected
{
  "patient": {
    "id": "27b3ceb0",
    "dob": "1875-11-03"
  },
  "symptoms": [
    "Nausea",
    ""
  ],
  "temperature": 12.5
}
errors4 problems
[patient/id] Invalid value `27b3ceb0` for Uuid
[patient/dob] Invalid value `1875-11-03` for Date (not alive)
[symptoms/1] Invalid value "" for String( s | s.size > 0 )
[temperature] Invalid value 12.5 for Temperature (celsius)

Coercing

Data exchange languages such as JSON impose a very low abstraction level: booleans, strings and numbers. Finitio helps raise the level of discourse, letting you navigate up and down abstraction levels through information contracts.

measures.json
[
  {
    "where": "Brussels",
    "at": "2014-03-01",
    "temperature": 13.5
  },
  {
    "where": "Paris",
    "at": "2014-02-27",
    "temperature": 12.0
  }
]
measures.fio
Measure = .Measure <info> {
  where: String,
  at: Date,
  temperature: Float( f | f >= -40.0 and f <= 50.0 )
}
[Measure]

There are two information contracts at work here. An implicit one, Date, provided by Finitio itself, and another one, Measure, connecting a tuple type to a Measure class.

In Finitio-rb, the Ruby binding, loading and dressing the JSON above returns an array of Measure instances. Dates are coerced along the way:

irbdressed
[
   #<Measure:0x007fb5d3a1ba40 @where="Brussels", @at=#<Date: 2014-03-01>, @temperature=13.5>,
   #<Measure:0x007fb5d3a16450 @where="Paris", @at=#<Date: 2014-02-27>, @temperature=12.0>
]

What you get

A type is a set of values

No fixed catalogue of booleans, strings and integers. Finitio gives you an abstract way of building information types: subtypes are subsets, supertypes are supersets. That is the whole definition.

Constraints, not just shapes

Attach named predicates to any type. A temperature is not a Real, it is a Real between 33.0 and 45.0 — and when a document fails, the error names the constraint it broke.

Information contracts

Dress low-level exchange data into rich host-language values, and undress it back. JSON has no date type; your program should not have to care.

Tuples, relations, unions, ADTs

Structural types borrowed from the relational model rather than from object orientation, because the goal is to capture information rather than behaviour.

Errors you can act on

Validation reports every problem with the path that located it, so a rejected payload tells you which field, which element, which constraint.

Ruby and JavaScript

finitio-rb implements the full specification. finitio.js brings the same schemas to Node and to the browser — it is what powers the playground on this site.

Write a schema, paste a document

The playground runs finitio.js entirely in your browser. Nothing is sent anywhere.

Open the playground