Slide 1

Slide 1 text

Making a simple Purescript Application Going from Node + Elm 0.16 to Purescript + Node FFI to write a torrent scraper

Slide 2

Slide 2 text

Why a torrent scraper? Don’t trust any “solutions” Want to run this on a VPS (with just Node) Just need magnet links downloaded Don’t want to do this manually I’m forgetful (want to run on CRON, etc)

Slide 3

Slide 3 text

Callback JS soup version (Worst of all worlds) RxJS version, JS still sucks Node + Elm version Elm for logic, but mechanics still separate and Rx powered Still requires startup of Elm runtime Ports interface still not so great Written in 0.16, 0.17 removed Signals, upgrade woes Previous versions?

Slide 4

Slide 4 text

Why Purescript? Want more compiler-checked code Want real FFI Want main program mechanics to not be in JS Signals (FRP library in Elm) removed in 0.17, making interfacing and writing programs more annoying Want to remove more of my own code as I can Purescript is way cooler

Slide 5

Slide 5 text

Quick introduction to PS FP lang inspired by Haskell Some big usability improvements Compiles to legible, real JS with no runtime Gives you real FFI More granular “Effects” system compared to Haskell’s “IO” main :: forall e. Eff (err :: EXCEPTION, get :: HTTP, post :: HTTP, console :: CONSOLE | e) Unit

Slide 6

Slide 6 text

Porting Elm to PS Most Elm code stays the same (other than :/::, Elm List -> JS/PS Array), but gets suped-up Get to use real FFI, no awkward runtime/port interface Control through Aff (async effects, basically `ErrorT (ContT Unit (Eff e) a`) Typeclasses like Functor (aka Interfaces like IMappable) Introduce fine-grained Effects

Slide 7

Slide 7 text

Real FFI --- Main.js --- exports.getTargetsPage = function (callback) { return function (url) { return function () { request(url, function () { [...] return callback(body)(); [...] exports.scrapeHtml = function (selector) { return function (html) { var $ = cheerio.load(html); var targets = []; $(selector).each(function (i, e) { [...] return targets.reverse(); --- Main.purs --- type Url = String type Selector = String type HtmlBody = String foreign import data HTTP :: ! foreign import scrapeHtml :: Selector -> HtmlBody -> FetchedTargets foreign import getTargetsPage :: forall e. (HtmlBody -> Eff (http :: HTTP | e) Unit) -> Url -> Eff (http :: HTTP | e) Unit

Slide 8

Slide 8 text

Control through Aff No need for juggling callbacks or whatever Aff (effects) Thing - “I am an asynchronous routine of (effects) returning (Thing)” getConfig :: forall e. Aff (fs :: FS | e) Config getDownloadedFiles :: forall e. Aff (fs :: FS | e) (Array FilePath) getFetchedTargets :: forall e. Url -> Selector -> Aff (http :: HTTP | e) FetchedTargets kickOffDownloads' :: forall e. DownloadTargets -> Aff (console :: CONSOLE | e) Unit main = launchAff $ do {url, selector, blacklist} <- getConfig downloadedFiles <- getDownloadedFiles fetchedTargets <- getFetchedTargets url selector kickOffDownloads' $ getDownloadTargets blacklist downloadedFiles fetchedTargets

Slide 9

Slide 9 text

Typeclasses Programming to interfaces with generic types is awesome But can’t do non-built-in comparable types in Elm E.g. how do you make a Set with generic types? Elm: Instantiate collection w/ concrete ordering/compare function? https://github. com/eeue56/elm-all-dict Purescript: Use a collection using the Ord typeclass, then derive or implement Ord (like purescript- sets) OR Roll your own collection easily, define your own typeclass, have instances to back it

Slide 10

Slide 10 text

Creating/Using Typeclasses foreign import data CrappyHashSet :: * -> * class CrappyHash a where crappyHash :: a -> String data Coords = Coords Int Int instance crappyHashCoords :: CrappyHash Coords where crappyHash (Coords x y) = "x:" ++ (show x) ++ "y:" ++ (show y) foreign import empty :: forall a. CrappyHashSet a foreign import insert :: forall a. (CrappyHash a) => a -> CrappyHashSet a -> CrappyHashSet a foreign import mapToArray :: forall a b. (a -> b) -> CrappyHashSet a -> Array b

Slide 11

Slide 11 text

Functor - “Functors can be thought of as homomorphisms between categories.” - Wikipedia Homomorphisms - “Structure-preserving operation”, i.e. map Categories - “‘Arrow’”-linked ‘objects’”, e.g. array <$> (infix map) - “I am a LOW priority operator for which I take the function on my left and map it to the thing on my right” Can be used with anything with an instance of Functor defined instance functorAff :: Functor (Aff e) where map f fa = [...] class Functor f where map :: (a -> b) -> f a -> f b

Slide 12

Slide 12 text

Functor in Action foreign import parseConfigFile :: String -> Config foreign import configPath :: String getConfig :: forall e. Aff (fs :: FS | e) Config getConfig = parseConfigFile <$> readTextFile UTF8 configPath foreign import scrapeHtml :: Selector -> HtmlBody -> FetchedTargets foreign import getTargetsPage :: forall e. (HtmlBody -> Eff (http :: HTTP | e) Unit) -> Url -> Eff (http :: HTTP | e) Unit getFetchedTargets :: forall e. Url -> Selector -> Aff (http :: HTTP | e) FetchedTargets getFetchedTargets url selector = scrapeHtml selector <$> makeAff (\e s -> getTargetsPage s url)

Slide 13

Slide 13 text

Fine-grained Effects “Side”-effects as part of the signature as extensible records -- Filesystem Effects: -- getConfig :: forall e. Aff (fs :: FS | e) Config getDownloadedFiles :: forall e. Aff (fs :: FS | e) (Array FilePath) -- HTTP Effects: -- getFetchedTargets :: forall e. Url -> Selector -> Aff (http :: HTTP | e) FetchedTargets -- Console Effects: -- foreign import kickOffDownloads :: forall e. DownloadTargets -> Eff (console :: CONSOLE | e) Unit kickOffDownloads' :: forall e. DownloadTargets -> Aff (console :: CONSOLE | e) Unit -- Our Main using all of these effects: -- main :: forall e. Eff (err :: EXCEPTION, fs :: FS, http :: HTTP, console :: CONSOLE | e) Unit

Slide 14

Slide 14 text

Results ✓ Works when source compiles ✓ Real FFI, no more runtime/port interfaces ✓ Contains the main mechanics of the program ✓ Can write code to typeclasses and their instances ✓ JS code reduced/minimized ✓ Is cool

Slide 15

Slide 15 text

Etc Repo: https://github.com/justinwoo/torscraper Purescript: http://www.purescript.org/ https://leanpub.com/purescript/read https://github.com/purescript/purescript/wiki/Language-Guide Might consider: http://haskellbook.com/ My spam channel: @jusrin00