Slide 1

Slide 1 text

PureScript & Halogen Vladimir Ciobanu Tuesday, May 8, 2018 Development Lead, Visual Solutions, Mood Media Romania

Slide 2

Slide 2 text

Overview Why Not... Introduction to PureScript My PureScript Workflow Halogen Conclusion 1

Slide 3

Slide 3 text

Why Not...

Slide 4

Slide 4 text

Why Not JavaScript? • very loose language (coercions, quirks, etc) • easy to hack something together, hard to keep it sane • practically inexistent type system • npm is a mess • frequent breaking changes 2

Slide 5

Slide 5 text

What About TypeScript? TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. • fails to fix a lot of problems (coercions, quirks are still there) • type definitions aren’t trivial to keep in sync • still missing a lot of advanced features (sum types, type classes, dependent types, etc) 3

Slide 6

Slide 6 text

Introduction to PureScript

Slide 7

Slide 7 text

What is PureScript? PureScript is a strongly-typed functional programming language that compiles to JavaScript. • Compile to readable JavaScript • Strong FFI / interoperability with JavaScript • No runtime (unlike Elm, GHCJS, etc) • Very good tooling • Great community • High-quality libraries 4

Slide 8

Slide 8 text

Syntax - Functions 1 addOne :: Int → Int 2 addOne x = x + 1 3 4 head :: List Maybe 5 head = case _ of 6 Nil → Nothing 7 Cons x _ → Just x 8 9 even :: Int → Boolean 10 not :: Boolean → Boolean 11 12 notEven :: Int → Boolean 13 notEven = not <<< even 14 15 headNotEven :: List Int → Maybe Boolean 16 headNotEven = map notEven <<< head 5

Slide 9

Slide 9 text

JavaScript Output 1/2 Head.purs 1 head :: List Maybe 2 head = case _ of 3 Nil → Nothing 4 Cons x _ → Just x output.js 1 var head = function (v) { 2 if (v instanceof Data_List_Types.Nil) { 3 return Data_Maybe.Nothing.value; 4 }; 5 if (v instanceof Data_List_Types.Cons) { 6 return new Data_Maybe.Just(v.value0); 7 }; 8 throw new Error("Failed pattern match at Main.."); 9 }; 6

Slide 10

Slide 10 text

JavaScript Output 2/2 HeadNotEven.purs 1 headNotEven :: List Int → Maybe Boolean 2 headNotEven = map notEven <<< head output.js 1 var headNotEven = function ($5) { 2 return Data_Functor.map 3 (Data_Maybe.functorMaybe)(notEven)(head($5)); 4 }; 7

Slide 11

Slide 11 text

Syntax - Records 1 type Person r = 2 { name :: String 3 , age :: Int 4 | r 5 } 6 7 isOlderThan :: ∀ r1 r2. Person r1 → Person r2 → Boolean 8 isOlderThan p1 p2 = p1.age > p2.age 9 10 isOlderThan’ :: ∀ r1 r2 11 . { age :: Int | r1 } 12 → { age :: Int | r2 } 13 → Boolean 14 isOlderThan’ p1 p2 = p1.age > p2.age 8

Slide 12

Slide 12 text

Effects 1 main :: Eff _ Unit 2 main = log "Hello world" 3 4 getElementById :: String → Eff _ (Maybe Element) 5 getElementById s 6 = ( querySelector (QuerySelector s) 7 <<< htmlDocumentToParentNode 8 <=< document 9 ) =<< window 10 11 window :: Eff _ Window 12 document :: Window -> Eff _ HTMLDocument 13 htmlDocumentToParentNode :: HTMLDocument → ParentNode 14 querySelector :: QuerySelector 15 → ParentNode 16 → Eff _ (Maybe Element) 9

Slide 13

Slide 13 text

Foreign Function Interface ParentNode.js 1 exports._querySelector = function (selector) { 2 return function (node) { 3 return function () { 4 return node.querySelector(selector); 5 }; }; }; ParentNode.purs 1 foreign import _querySelector :: QuerySelector 2 → ParentNode 3 → Eff _ (Nullable Element) 4 5 querySelector :: QuerySelector 6 → ParentNode 7 → Eff _ (Maybe Element) 8 querySelector qs = map toMaybe <<< _querySelector qs 10

Slide 14

Slide 14 text

My PureScript Workflow

Slide 15

Slide 15 text

Tooling Demo • pulp init • pursuit • bower install • vscode 11

Slide 16

Slide 16 text

Halogen

Slide 17

Slide 17 text

Basic Component 1 data Query a = DoNothing a 2 type Input = Unit; type Message = Void; type State = Unit 3 4 component :: ∀ m. H.Component HH.HTML Query Input Message m 5 component = H.component 6 { initialState: id 7 , render 8 , eval 9 , receiver: const Nothing 10 } 11 where 12 13 render :: State → H.ComponentHTML Query 14 render _ = HH.text "Hello, world" 15 16 eval :: Query H.ComponentDSL State Query Message m 17 eval (DoNothing next) = pure next 12

Slide 18

Slide 18 text

Render 1 render :: State → H.ComponentHTML Query 2 render st = 3 HH.div 4 [ HP.class_ (H.ClassName "messages__middle") ] 5 [ HH.div 6 [ HP.class_ (H.ClassName "messages") ] 7 $ (map (renderMessage st.playing) st.items) 8 <> guard st.isSchedule 9 [ HH.button 10 [ HE.onClick <<< HE.input_ $ Commit 11 , HP.class_ <<< H.ClassName $ "messages-btn" 12 , HP.disabled $ not st.isDirty 13 ] 14 [ HH.text "Save" ] 15 ] 16 ] 13

Slide 19

Slide 19 text

Eval 1 eval :: Query H.Component HH.HTML Query Input Message MsgM 2 eval = case _ of 3 Initialize next → next <$ do 4 H.fork do 5 env ← unMessagesEnvironment <$> H.lift ask 6 tags → H.lift <<< liftServer $ getTags 7 isSchedule ← H.lift $ _.isSchedule <$> get 8 case tags of 9 Left err → H.lift <<< navigate $ BadSetup 10 Right t → H.put $ Just 11 { tags: t 12 , selectedTag: SchedOrDmd isSchedule <<< unwrap $ t 13 , localeService: env.localeService 14 , isSchedule: isSchedule 15 } 14

Slide 20

Slide 20 text

Conclusion

Slide 21

Slide 21 text

Resources • Christopher Allen, The Haskell Book http://haskellbook.com • Phil Freeman, PureScript by Example http://leanpub.com/pursecript • Pursuit, PureScript Documentation http://pursuit.purescript.org • Vladimir Ciobanu, Halogen Example http://github.com/vladciobanu/purescript-halogen-example • Functional Programming Slack https://fpchat-invite.herokuapp.com/ • PureScript Discourse http://purescript-users.ml 15

Slide 22

Slide 22 text

We Are Hiring is the global leader for Experience Design services. The Visual Solutions is in charge of our Digital Signage product. We are migrating key components to Haskell and PureScript. Are you: • passionate about writing software? • interested in learning and use functional programming? If you answered yes to both questions, then you should join our team! No prior experience with FP is required. 16

Slide 23

Slide 23 text

Thank You Thank you for listening! cvlad vladciobanu cvlad.info 17