framework for predictable code” Nice utility “framework” for building my apps as a cycle of streams Nicely separates what effects happen from my state Able to use power of reactive programming (flatMap!!!, etc.) No single/chain reaction action dispatch queue
constructor tricks in TS2/Flow32+) Sink/driver matching by string key is annoying & manual Feels like type annotations take a lot of work to add I like having one stream that I can split/transform however I want In dev, I want to log all driver commands easily
We know which position at which sink/driver interaction is expected Easy enough to type, I guess (need redundant type sigs) function run<A1, A2>( main: (sources: [A1]) => [A2], drivers: [(sink: A2) => A1]) function run<A1, A2, B1, B2>( main: (sources: [A1, B1]) => [A2, B2], drivers: [(sink: A2) => A1, (sink: B2) => B1])
Grows linearly and is ugly!!! Tuples aren’t fun to work (indices have no meaning) Separate streams with driver per command stream I want to access whichever, whenever!
as I wish Cram everything together! Since return value main function is a stream… Algebraic Data Type approximation! People now very familiar with “union types” anyhow Using “type” tagged objects (At the very least, 100% JSON compatible)
run<A, B>( main: (source: A) => Stream<B>, driver: (sink: Stream<B>) => A) Where A is my source from my “megadriver” B is my stream of commands to the world
to DOM 2. Print things to console.log Then we need to 1. Create some tagged objects for each command 2. Create a union type for these type Command = DOMCommand | ConsoleCommand type DOMCommand = { type: 'DOMCommand', payload: { node: VNode } }; type ConsoleCommand = { type: 'ConsoleCommand', payload: { text: string } };
But at least now everything is simpler! Less work to annotate TS & Flow can do everything for us We can even log out all of the commands for easy debugging, Redux style
sink and driver input Tagged object unions are easy to work with This makes TS and Flow more helpful as a result No need for complicated keyOf/valueOf type constructors
in Purescript Dynamic records are too much of a pain Now I have this megadriver approach Integrating JS code through FFI: easy peasy Want to port my ex-Cycle Purescript app to Purescript-Cycle!
evaluated, no runtime (pretty small output as a result) Already in production use at many companies In “production” on my VPS for 1 year Great Foreign Function Interface Write as little or much Javascript as you want! What is Purescript?
run :: forall e a b. (a -> Stream b) -> (Stream b -> Eff e a) -> Eff e (Dispose e) run = runFn2 _run foreign import _run :: forall e a b. Fn2 (a -> Stream b) (Stream b -> Eff e a) (Eff e (Dispose e)) ...and 23 lines of FFI JS to handle effs: Eff: thunked functions e.g. _driver(sink)()
1. Check torrents on a timer or per request “get” 2. Respond to location messages with directions home using HSL API data Query = ScrapeRequest | TimerRequest | LastTrainRequest RequestWithOrigin | QueueMessage Result data Command = ScrapeCommand RequestOrigin | LastTrainCommand RequestWithOrigin | SendMessage Result | Info String
= inner <$> queries where inner = case _ of TimerRequest -> ScrapeCommand Timer ScrapeRequest -> ScrapeCommand User LastTrainRequest req -> LastTrainCommand req QueueMessage result@{origin, output} -> do case Tuple origin (indexOf (Pattern "nothing new") output) of Tuple Timer (Just _) -> Info "timer found nothing" _ -> SendMessage result
:: forall e. Config -> Stream Command -> Eff (MyEffects e) (Stream Query) main = launchAff $ do runExcept <$> getConfig >>= case _ of Left e -> AffC.log $ "config.json is malformed: " <> show e Right config -> liftEff <<< void $ run main_ (driver config)
Type check main function and drivers in run() https://github.com/cyclejs/cyclejs/issues/404 Simplify drivers by making it a hodgepodge? https://github.com/cyclejs/cyclejs/issues/432