Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
Multiple items
type MeasureAttribute =
  inherit Attribute
  new : unit -> MeasureAttribute

Full name: Microsoft.FSharp.Core.MeasureAttribute

--------------------
new : unit -> MeasureAttribute
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
type unit = Unit

Full name: Microsoft.FSharp.Core.unit

Designing composable functional libraries
not just for visualization

Tomas Petricek

University of Kent & fsharpWorks
tomas@tomasp.net | @tomaspetricek

Motivation

Functional thinking about charts

What is this
talk about?

Making facts great again

Building more open, transparent and engaging data visualization

What is this
talk about?

Functional thinking!

New look at an interesting and tricky domain

What is a chart?

A very long list...

Bar chart
Column chart
Line chart
Area chart
Scatter chart
Histogram
Combo chart???

What is a chart?

Uh...

Maybe chart is just an SVG graphics with text, shapes and pixel coordinates?

What is a chart?

D3 is a too low-level answer

1: 
2: 
3: 
4: 
x = d3.scaleLinear([0, m - 1], [0, width])
y = d3.scaleLinear([0, 1], [height, 0])
z = d3.interpolateCool
d3.area().x((d, i) => x(i)).y0(d => y(d[0])).y1(d => y(d[1]))

Google Charts is a too high-level answer

1: 
2: 
3: 
4: 
var options = {
  vAxis: {title: 'Cups'}, hAxis: {title: 'Month'},
  seriesType: 'bars', series: {5: {type: 'line'}} };
chart.draw(data, options);

What is a chart?

Also interactivity!

A chart where the reader has to make a guess before seeing the answer.

What is a chart

Fundamentals of a chart

Projections from domain values to pixels

Shapes such as areas and lines

Composition of multiple shapes and text

Interactivity state depends on user input

Composing shapes

Fundamentals of a chart

DEMO

Creating a bar chart

Scales

Continuous and categorical scales

Scales

Continuous and categorical scales

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type Value =
  | Categorical of string * float
  | Continuous of float

type Scale =
  | Continuous of float * float
  | Categorical of string[]

Modelling charts

A chart is an algebraic data type

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type Shape =
  | Style of (Style -> Style) * Shape
  | Line of seq<Value * Value>
  | Shape of seq<Value * Value>
  | Axes of (bool * bool * bool * bool) * Shape
  | Padding of (float * float * float * float) * Shape
  | Layered of seq<Shape>

Modelling charts

Avoiding X and Y value mix-up!

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type Shape<[<Measure>] 'vx, [<Measure>] 'vy> =
  | Style of (Style -> Style) * Shape<'vx, 'vy>
  | Line of seq<Value<'vx> * Value<'vy>>
  | Shape of seq<Value<'vx> * Value<'vy>>
  | Axes of (bool * bool * bool * bool) * Shape<'vx, 'vy>
  | Padding of (float * float * float * float) * Shape<'vx, 'vy>
  | Layered of seq<Shape<'vx, 'vy>>

Projections

From domain space to pixel space

1: 
type Projection<'vx, 'vy, 'ux, 'uy> = (...)
1: 
2: 
3: 
4: 
5: 
val project :
     Scale<'vx> * Scale<'vy>
  -> Value<'vx> * Value<'vy>
  -> Projection<'vx, 'vy, 'ux, 'uy>
  -> Value<'ux> * Value<'uy>

DEMO

Line chart with background

DEMO

Adding a chart title

Composition

There are more ways than one!

1: 
2: 
3: 
4: 
5: 
type Shape<[<Measure>] 'vx, [<Measure>] 'vy> =
  | (* ... *)
  | InnerScale of Scale<'vx> * Scale<'vy> * Shape<'vx, 'vy>
  | OuterScale of Scale<'vx> * Scale<'vy> * Shape<'vx, 'vy>
  | Layered of seq<Shape<'vx, 'vy>>

Domain modelling

Functional thinking about charts

Domain primitives rather than graphics primitives

Domain values rather than pixels!

Composition in multiple different ways

Units of measure so that I can implement it :-)

Library design

Three functional design patterns

DEMO

Refactoring chart title

Layers of abstraction

From charts to pixels

Transformations

Shape rendering pipeline

DEMO

Creating animated bar chart

State + Update

Elm-based application architecture

Implementation

JavaScript libraries in F#

Wait, not everyone uses F# & Fable?

Add a lightweight wrapper API

Compile to plain JavaScript library!

Supporting JavaScript

Lightweight wrapper API

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
type JsCompost =
  abstract nest : obj * obj * obj * obj * Shape -> Shape
  abstract scale : Scale * Scale * Shape -> Shape
  abstract overlay : Shape[] -> Shape
  abstract font : string * string * Shape -> Shape
  abstract column : string * float -> Shape
  abstract bar : float * string -> Shape
  abstract shape : obj[][] -> Shape
  abstract line : obj[][] -> Shape
  abstract axes : string * Shape -> Shape
  abstract render : string * Shape -> unit
  // (and a few more but not too many!)

DEMO

Using Compost from JavaScript

Summary

Functional thinking about charts

Composable libraries

Fundamental question
What is the thing we're working with?

Domain modelling
Primitives and composition (with units)

Functional patterns
Multiple layers, transformations, states and updates


Tomas Petricek, University of Kent & fsharpWorks
tomas@tomasp.net | @tomaspetricek