Making a simple Purescript Application

Making a simple Purescript Application

Small talk for a weekly live/web programming meetup at Futurice in Helsinki (and Tampere, London, Berlin)

On Google Docs: https://docs.google.com/presentation/d/1n-I4IlBgUzRyJfVMaOJh-wz1j1Le7MyFwgwr_meX0Uo/edit?usp=sharing

3b48c91bf6b6f0bfd0fda50625598656?s=128

Justin Woo

May 20, 2016
Tweet

Transcript

  1. Making a simple Purescript Application Going from Node + Elm

    0.16 to Purescript + Node FFI to write a torrent scraper
  2. 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)
  3. 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?
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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)
  13. 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
  14. 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
  15. 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