class: center, middle # Explicitly Comprehensible Functional Reactive Programming ## Steve Krouse --- class: center, middle # This talk is about the **comprehensibility** of large FRP applications. --- class: center, middle # This this talk does not have The Answer
TM
--- # FP is comprehensible ## 1) Local reasoning Code elsewhere cannot affect this code via *side-effects*.
## 2) Explicit data dependencies We can grok this code by reading its dependency definitions recursively. --- # FP is not *always* comprehensible --- # FP is not *always* comprehensible > There is in principle nothing to stop functional programs from **passing a single extra parameter into and out of every single function** in the entire system. If this extra parameter were a **collection (compound value)** of some kind then it could be used to **simulate an arbitrarily large set of mutable variables**... > **...ease of reasoning is lost** (we still know that each function is dependent only upon its arguments, but one of them has become so large and **contains irrelevant values** that the benefit of this knowledge as an aid to understanding is almost nothing). This is however **an extreme example** and does not detract from the general power of the functional approach > -- Out of the Tarpit (Moseley and Marks) --- class: center, middle # This "extreme example" has become the *most popular* pattern in FRP! --- class: center, middle # The Elm Architecture
--- # Elm Counter
+1
0
-1
``` 01 type alias Model = { count : Int } 02 03 initialModel : Model 04 initialModel = { count = 0 } 05 06 type Msg = Increment | Decrement 07 08 update : Msg -> Model -> Model 09 update msg model = case msg of 10 Increment -> { model | count = model.count + 1 } 11 Decrement -> { model | count = model.count - 1 } 12 13 view : Model -> Html Msg 14 view model = div [] 15 [ button 16 [ onClick Increment ] 17 [ text "+1" ] 18 , span [] [ text <| toString model.count ] 19 , button 20 [ onClick Decrement ] 21 [ text "-1" ] 22 ] ``` --- # Elm Todo MVC
``` Add -> { model | uid = model.uid + 1 , field = "" , entries = if String.isEmpty model.field then model.entries else model.entries ++ [newEntry model.field model.uid ] } ``` --- # Elm TodoMVC Ctl-F "entries ="
--- # Elm TodoMVC Ctl-F "entries =" messages
--- # Elm TodoMVC diagram
--- # Reflex FRP haskell, ghcjs ## 1) higher order ## 2) cyclic --- # Reflex Counter ``` 01 button :: Text -> m (Event ()) 02 el :: Text -> m a -> m a 03 display :: Show a => Dynamic a -> m () 04 (<$) :: a -> Event b -> Event a 05 leftmost :: [Event a] -> Event a 06 foldDyn :: (a -> b -> b) -> b -> Event a -> m (Dynamic b) 09 08 bodyElement :: MonadWidget t m => m () 09 bodyElement = do 10 rec evIncr <- button "+1" 11 el "div" $ display count 12 evDecr <- button "-1" 13 count <- foldDyn (+) 0 $ leftmost 14 [ 1 <$ evIncr 15 , -1 <$ evDecr 16 ] 17 return () 18 19 main :: IO () 20 main = mainWidget bodyElement ``` --- # Reflex TodoMVC Entries ``` 01 initialTasks :: Map Int Task 02 initialTasks = Map.empty 03 04 insertNew_ :: Task -> Map Int Task -> Map Int Task 05 06 todoMVC :: (DomBuilder t m, MonadFix m, MonadHold t m) => m () 07 todoMVC = do 08 el "div" $ do 09 elAttr "section" ("class" =: "todoapp") $ do 10 mainHeader 11 rec tasks <- foldDyn ($) initialTasks $ 12 mergeWith (.) 13 [ fmap insertNew_ newTask 14 , listModifyTasks 15 , fmap (const $ Map.filter $ not . taskCompleted) clearCompleted 16 ] 17 newTask <- taskEntry 18 listModifyTasks <- taskList activeFilter tasks 19 (activeFilter, clearCompleted) <- controls tasks 20 return () 21 infoFooter ``` Cycle: `tasks` depends upon `listModifyTasks` and `clearCompleted`, which depend upon `tasks` --- # Reflex TodoMVC Diagram
--- # Reflex TodoMVC Diagram (Simplified)
--- # Reflex is not The Answer
TM
### - Reflex + ghcjs is annoying ### - Elm is pleasant ### - The Elm Architecture model is serializable -> time travel debugging, hot reloading --- # Conclusion ## I hope you are now _unhappy_ with where we are with state management in FRP. And excited to look for other solutions. Such a _higher-order_ and _cyclic_ streams.