Tom Murphy

FARM 2016

- Running program changed by the programmer(s) at runtime
- Core operation:
*hot-swap* - GHCi
- Erlang/OTP
- Art!

- Purity (very limited I/O)
- Big Ball of State

- We'd like to do I/O
- MIDI instruments
- Playing music

- ...without throwing up our hands

And we'd like to be able to hotswap

Experiment, tweak params, add stuff and throw stuff away, freeze/hold, ...

` fmap :: Functor f => (a -> b) -> f a -> f b`

```
chars = "midair" :: [Char]
fmap (=='a') chars :: [Bool]
```

```
getChar :: IO Char
fmap (=='a') getChar :: IO Bool
```

Pseudocode:

```
lastKeyPressed :: FRP Char
fmap (=='a') lastKeyPressed :: FRP Bool
```

- Controlled state
- With hot-swap?

- General semantics
- FRP with first-class hot-swap

- Our formulation:
**Midair**- Implementation
- Idioms

` data SFlow a b`

- Push-based
- "Fire"

Discrete time

E-FRP, RT-FRP

` data SFlow a b`

```
sMap :: (a -> b) -> SFlow a b
sFold :: c -> (a -> c -> c) -> SFlow a c
sFilter :: (b -> Bool) -> SFlow b b
sCompose :: SFlow b c -> SFlow a b -> SFlow a c
-- ...
```

```
instance Applicative (SFlow a)
instance Arrow SFlow
```

```
sFold True (\_ -> not) <<< sFilter (==' ')
:: SFlow Char Bool
```

(demo!)

```
width :: SFlow x Double
height :: SFlow x Double
area :: SFlow x Double
area = (*) <$> width <*> height
-- Note it's deterministic
```

```
do w <- width
h <- height
pure (w * h)
```

Including any sub-component

- "Folding over the past"
- How much past?

```
data Direction = Up | Down
volChange :: Double -> Direction -> Double
volChange v Up = v * 1.01
volChange v Down = v * 0.99
```

```
buttonHistory :: [Direction] -- 100 up, 50 down
foldl volChange 1 buttonHistory == 1.65...
```

```
volChange' v Down = v * 0.97
volChange' v Up = v * 1.03
foldl volChange' 1 buttonHistory == 4.19...
```

- Very constrained domain:
- Succinct
- Inference is important

- Efficient code
- Even when not optimized

"Compiled code is roughly 10x faster than interpreted code, but takes about 2x longer to produce (perhaps longer if optimisation is on)." - From the GHC Users' Manual

- Takeaway: write code that's fast/cheap when not compiled
*Compile time***is**runtime

(Simplified:)

```
data SFlow a c where
SF_Map :: (a -> c) -> SFlow a c
SF_Compose
:: forall a b c.
SFlow b c -> SFlow a b -> SFlow a c
SF_Fold :: c -> (a -> c -> c) -> SFlow a c
SF_Filter
:: Maybe a -> (a -> Bool) -> SFlow a a
SF_NodeRef
:: TVar (Maybe a) -> TVar (Maybe c)
-> TVar (SFlow a c) -> SFlow a c
```

- Firing walks the tree
- (basically!)

- We'll see
`SF_NodeRef`

soon

`fireGraph :: TVar (SFlow i o) -> i -> STM (Maybe o)`

`STM`

for graph update`Maybe`

for`sFilter`

- That's all I'm going to say about that!

- So far we've only seen static FRP graphs!

```
mkNodeRef :: SFlow a b -> IO (SFNodeRef a b)
nRef :: SFNodeRef a b -> SFlow a b
hotswap :: SFNodeRef a b
-> (Maybe a -> Maybe b -> SFlow a b)
-> IO ()
```

- Explicit references
- Can be used everywhere
- Included nested

```
fmap nRef (mkNodeRef x)
/=
pure x
```

```
ref <- mkNodeRef $ sMap (transpose 2)
:: IO (SFNodeRef Chord Chord)
```

```
sMap playChord <<< nRef ref <<< sMap makeChord
:: SFlow Note (IO ())
```

` hotswap ref $ \_ _ -> sMap (transpose 4)`

```
ref <- mkNodeRef $ sFold 1 volChange
hotswap ref $ \_ lastOut ->
sFold (fromMaybe 1 lastOut) volChange'
```

```
hotswap ref $ \_ (Just lastOut) ->
sFold lastOut volChange'
```

` hotswapFold ref 1 volChange'`

- "Pivot"!

```
-- | Gives the first item that passes
sFind :: (a -> Bool) -> SFlow a a
sTake :: Int -> SFlow a [a]
sAll :: (a -> Bool) -> SFlow a Bool
sElem :: Eq a => a -> SFlow a Bool
sMapMaybe :: (a -> Maybe b) -> SFlow a b
sRights :: SFlow (Either l r) r
sPrint :: Show a => SFlow a (Fx ())
sSum :: Num n => SFlow n n
sMax :: Ord a => SFlow a a
hotswapHold :: SFNodeRef x a -> IO ()
hotswapFold :: SFNodeRef a c -> c -> (a -> c -> c) -> IO ()
hotswapCount :: SFNodeRef x n -> IO ()
-- ...
```

We can demonstrate its use using `sCount`

:

```
sCount :: Num n => SFlow x n
== sSum <<< sConst 1
== sFold (+) 0 <<< sMap (const 1)
```

We can, given keyboard input, count every key press of a digit character:

```
ref <- mkNodeRef sCount
nRef ref <<< sFilter isDigit
:: SFlow Char Int
```

Then stop counting for a while:

` hotswapHold ref`

Then, after more time, start counting where we left off:

` hotswapCount ref`

Even after switching back to counting fires with "count," none of the key presses that occurred while the "hold" was in effect are part of the sum emitted from the graph.