Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift to Haskell: Overloading Semicolons

Swift to Haskell: Overloading Semicolons

v1 at SE2017, v2 at CocoaHeadsKyiv December 2017

Volodymyr Kyrylov

October 27, 2017
Tweet

More Decks by Volodymyr Kyrylov

Other Decks in Programming

Transcript

  1. struct Team {} struct Tournament {} func runTournament(_ tournament: Tournament)

    -> Team { let teams = tournament.joinTeams(); let groups = tournament.emitGroups(teams); let groupResults = groups.map(tournament.runGroup); let brackets = tournament.buildBracket(groupResults); NSLog("%@", brackets); let winner = tournament.eliminate(brackets); return winner; }
  2. Distinct Haskell Features → laziness (non-strict eval, call-by-need) → pure,

    referentially-transparent functions → enables equational reasoning
  3. Why FP Matters, John Hughes 1990: Modularity within eps (a:b:rest)

    | abs(a-b) <= eps = b | otherwise = within eps (b:rest) easydiff f x h = (f(x+h) − f x)/h differentiate h0 f x = map (easydiff f x) (repeat (/2) h0) within eps (differentiate h0 f x)
  4. Graphs data Node = A | B | C |

    D | E | F | G | H | I | J | K | L | M | N deriving (Show, Enum, Bounded, Eq) edge from to = case (from, to) of (A, B) -> Just 4 (A, E) -> Just 6 (A, D) -> Just 7 -- ...
  5. reachable :: Node -> [Node] reachable from = catMaybes [fmap

    (const to) (edge from to) | to <- enum]
  6. type Trace = [Node] data Path = Path Int Trace

    deriving (Show, Eq) instance Ord Path where (Path a _) <= (Path b _) = a <= b
  7. Build the tree of all possible paths! bruteforce :: Int

    -> Trace -> Node -> Node -> Tree Path bruteforce cost trace to from = Tree (Path cost trace') (map build (reachable from)) where trace' = from:trace build = bruteforce (cost + fromJust (edge from n)) trace' to
  8. Branch and Bound data Bound a = Bound a |

    Unknown deriving (Show, Eq) -- | Unlike Maybe's Ord this one steers away from Unknown instance (Eq a, Ord a) => Ord (Bound a) where -- ...
  9. minbranch :: Ord a => Tree a -> Bound a

    minbranch = minbranch' Unknown minbranch' :: Ord a => Bound a -> Tree a -> Bound a minbranch' bound (Tree.Node root subs) = case subs of [] -> Bound root _ -> foldr f bound subs where f sub@(Tree.Node r _) b | Bound r <= b = branch' b sub | otherwise = b -- **prune !!!**
  10. struct Team {} struct Tournament {} func runTournament(_ tournament: Tournament)

    -> Team { let teams = tournament.joinTeams(); let groups = tournament.emitGroups(teams); let groupResults = groups.map(tournament.runGroup); let brackets = tournament.buildBracket(groupResults); NSLog("%@", brackets); let winner = tournament.eliminate(brackets); return winner; }
  11. a slight syntax conversion data Team data Tournament runTournament (tournament

    :: Tournament) = let teams = joinTeams tournament groups = emitGroups tournament teams groupResults = map (runGroup tournament) teams brackets = buildBracket tournament groupResults _ = nsLog brackets winner = eliminate tournament brackets in (winner :: Team)
  12. nsLog will never be executed! data Team data Tournament runTournament

    (tournament :: Tournament) = let teams = joinTeams tournament groups = emitGroups tournament teams groupResults = map (runGroup tournament) teams brackets = buildBracket tournament groupResults _ = nsLog brackets winner = eliminate tournament brackets in (winner :: Team)
  13. let's bring back data dependencies data Team data Tournament runTournament

    (τ :: Tournament) = joinTeams τ ! (\teams -> emitGroups τ teams ! (\groups -> map (runGroup τ) teams ! (\groupResults -> buildBracket τ groupResults (\brackets -> nsLog brackets ! (\_ -> eliminate τ brackets (\winner -> winner))))))
  14. what type should have? sanity check: nsLog :: Show a

    => a -> Int nsLog x = callCFunction "NSLog" "%@" x test = let action = nsLog "hello" in x ! (\_ -> x) * not referentially transparent!
  15. let's come up with a type for effects data Effects

    a = {- constructor for a computation that returns a value of type `a' -} ! :: Effects a -> (a -> Effects b) -> Effects b ! a f = {- perform computation `a', then feed its result to `f' which will give a computation `b' -} unit :: a -> Effects a unit a = {- make `a' pretend to be a computation -}
  16. data Team data Tournament runTournament :: Tournament -> Effects Team

    runTournament τ = joinTeams τ ! (\teams -> emitGroups τ teams ! (\groups -> map (runGroup τ) groups (\groupResults -> buildBracket τ groupResults (\brackets -> nsLog brackets ! (\_ -> eliminate τ brackets (\winner -> unit winner))))))
  17. Sugar: do runTournament τ = do teams <- joinTeams τ

    groups <- emitGroups τ groupResults <- mapM (runGroup τ) groups brackets <- buildBracket τ groupResults nsLog brackets winner <- eliminate τ brackets unit winner
  18. a monad, where = >>= class Functor f where fmap

    :: (a -> b) -> f a -> f b class Functor m => Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b also, m has kind * -> *: HKT!
  19. map in a monad mapM :: Monad m => (a

    -> m b) -> [a] -> m [b] mapM f [] = return [] mapM f (x:xs) = f x >>= \x' -> mapM f xs >>= \xs' -> return (x':xs')
  20. what about the rest of the functions? runTournament τ =

    do teams <- joinTeams τ groups <- emitGroups τ groupResults <- mapM (runGroup τ) groups brackets <- buildBracket τ groupResults nsLog brackets winner <- eliminate τ brackets unit winner
  21. meh joinTeams = Ѕ༼ ϑ ༽ꙷ emitGroups = Ѕ༼ ϑ

    ༽ꙷ runGroup = Ѕ༼ ϑ ༽ꙷ buildBracket = Ѕ༼ ϑ ༽ꙷ eliminate = Ѕ༼ ϑ ༽ꙷ
  22. types first data TournamentOps next = JoinTeams ([Team] -> next)

    | EmitGroups [Team] ([Group] -> next) | RunGroup Group (GResult -> next) | BuildBracket [GResult] (Bracket -> next) | Eliminate Bracket (ElimResult -> next)
  23. Free Monads ∀ f:Functor you have a simplest way to

    construct a Monad data Free f a = Return a | Free (f (Free f a))
  24. a free functor instance Functor f => Functor (Free f)

    where fmap f x = case x of Return a -> Return (f a) Free as -> Free (fmap (fmap f) as)
  25. and a free monad for every free functor instance Functor

    f => Monad (Free f) where return = Return (>>=) :: Free f a -> (a -> Free f b) -> Free f b x >>= f = case x of Return a -> f a Free as -> Free (fmap (>>= f) as)
  26. lifting to Free liftF :: Functor f => f a

    -> Free f a liftF = Free . fmap return act x = liftF . flip x () -- `act' for action fun0 x = liftF (x id) fun1 x = liftF . flip x id
  27. what about the rest? joinTeams = fun0 JoinTeams emitGroups =

    fun1 EmitGroups runGroup = fun1 RunGroup buildBracket = fun1 BuildBracket eliminate = fun1 Eliminate
  28. let's interpret it evalDemo :: Free Tournament ElimResult -> ElimResult

    evalDemo program = case program of Return x -> x Free (JoinTeams f) -> evalDemo (f mkTeam) Free (EmitGroups a f) -> evalDemo (f mkSomeGroup) Free (RunGroup (Group (w:r:_)) f) -> evalDemo (f (GResult w r)) Free (BuildBracket a f) -> evalDemo (f bracket3Demo) Free (Eliminate a f) -> evalDemo (f (Winner (Team 1)))
  29. chatbots data Query data Input data Dialog next = Ask

    Query (Input -> next) bot :: [Input] -> Free Dialog Input -> Int bot answers program = case program of Return x -> x Free (Ask q f) -> case answers of (x:xs) -> bot xs (f x) [] -> error "lol"
  30. a blast from the past DSLs in objc era regularExpressionWithPattern:

    predicateWithFormat: constraintsWithVisualFormat: make.left.equalTo(superview.mas_left) .with.offset(padding.left)
  31. why do this in a strict language? → OCaml's LWT:

    https://mirage.io/wiki/tutorial-lwt let start c = Lwt.join [ (Time.sleep_ns (Duration.of_sec 1) >>= fun () -> C.log c "Heads"); (Time.sleep_ns (Duration.of_sec 2) >>= fun () -> C.log c "Tails") ] >>= fun () -> C.log c "Finished"
  32. canny :: Float -> Float -> Acc (Image RGBA32) ->

    (Acc (Image Float), Acc (Vector Int)) canny (constant -> low) (constant -> high) = stage1 . nonMaximumSuppression low high . gradientMagDir low . gaussianY . gaussianX . toGreyscale where stage1 x = (x, selectStrong x) → accelerate
  33. data Maybe a = Nothing | Just a data List

    a = Nil | Cons a (List a)
  34. data Fix f = Fix (f (Fix f)) data L

    a b = Nil | Cons a b type List a = Fix (L a) Nat ∼ Fix Maybe
  35. data Free f a = Return a | Free (f

    (Free f a)) data Const c a = Const c data Free (Const c) a = Pure a | Free (Const c) data Either c a = Right a | Left c
  36. Decision Trees Are Free Monads Over the Reader Functor Clay

    Thomas https://clathomasprime.github.io/hask/freeDecision
  37. is all of this going to be in Swift? →

    probably not → but wait
  38. // Staged function representing f(x, w, b) = dot(x, w)

    + b let f: Rep<(Float2D, Float2D, Float1D) -> Float2D> = lambda { x, w, b in x • w + b } // Staged function ’g’, type-inferred from ’f’ let g = lambda { x, w, b in let linear = f[x, w, b] // staged function application return tanh(linear) } // Gradient of ’g’ with respect to arguments ’w’ and ’b’ let dg = gradient(of: g, withRespectTo: (1, 2), keeping: 0) // ’dg’ has type: // Rep<(Float2D, Float2D, Float1D) -> (Float2D, Float2D, Float2D)> // Call staged function on input data ’x’, ’w’ and ’b’ let (dg_dw, dg_db, result) = dg[x, w, b]