Slide 1

Slide 1 text

Performant polymorphism Rewrite rules in Haskell Fraser Tweedale @hackuador May 8, 2017

Slide 2

Slide 2 text

There’s a hole in my program. . . _ :: B.ByteString -> L.ByteString _ :: B.ByteString -> [Word8] _ :: T.Text -> String _ :: String -> T.Text _ :: TL.Text -> T.Text

Slide 3

Slide 3 text

There’s a hole in my program. . . L.fromStrict :: B.ByteString -> L.ByteString B.unpack :: B.ByteString -> [Word8] T.unpack :: T.Text -> String T.pack :: String -> T.Text TL.toStrict :: TL.Text -> T.Text

Slide 4

Slide 4 text

Control.Lens.Cons class Cons s t a b where _Cons :: Prism s t (a, s) (b, t) instance ByteString ByteString Word8 Word8 instance Text Text Char Char instance [a] [b] a b cons :: Cons s s a a => a -> s -> s uncons :: Cons s s a a => s -> Maybe (a, s)

Slide 5

Slide 5 text

recons recons :: (Cons s1 s1 a a, Cons s2 s2 a a, AsEmpty s2) => s1 -> s2

Slide 6

Slide 6 text

There’s a hole in my program. . . _ :: B.ByteString -> L.ByteString _ :: B.ByteString -> [Word8] _ :: T.Text -> String _ :: String -> T.Text _ :: TL.Text -> T.Text

Slide 7

Slide 7 text

There’s a hole in my program. . . recons :: B.ByteString -> L.ByteString recons :: B.ByteString -> [Word8] recons :: T.Text -> String recons :: String -> T.Text recons :: TL.Text -> T.Text

Slide 8

Slide 8 text

Why not concrete functions? Reuse Refactoring Readability (parametricity)

Slide 9

Slide 9 text

Parametricity mibbup :: (Cons s1 s1 a a, Cons s2 s2 a a, AsEmpty s2) => s1 -> s2 wossit :: [Word8] -> L.ByteString

Slide 10

Slide 10 text

Parametricity recons :: (Cons s1 s1 a a, Cons s2 s2 a a, AsEmpty s2) => s1 -> s2 L.pack :: [Word8] -> L.ByteString

Slide 11

Slide 11 text

criterion whnf :: (a -> b) -> a -> Benchmarkable nf :: NFData b => (a -> b) -> a -> Benchmarkable bench :: String -> Benchmarkable -> Benchmark

Slide 12

Slide 12 text

Glasgow Haskell Compiler Haskell desugars to Core Simplifier applies Core-to-Core transformations Core → STG → C-- Compile C-- to machine code Secrets of the Haskell Inliner: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/inline-jfp.pdf Compilation by transformation: https://www.microsoft.com/en-us/research/wp-content/uploads/1998/09/comp-by-trans-scp.pdf

Slide 13

Slide 13 text

Phase control -- Before phase 2 Phase 2 and later {-# INLINE f #-} -- Yes Yes {-# NOINLINE f #-} -- No No {-# INLINE [2] f #-} -- No Yes {-# INLINE [~2] f #-} -- Yes No {-# NOINLINE [2] f #-} -- No Maybe {-# NOINLINE [~2] f #-} -- Maybe No

Slide 14

Slide 14 text

Rewrite rules {-# RULES "map/map" forall f g xs. map f (map g xs) = map (f . g) xs #-}

Slide 15

Slide 15 text

Rewrite rules {-# RULES "map/map" [2] forall f g xs. map f (map g xs) = map (f . g) xs #-}

Slide 16

Slide 16 text

Rewrite rules LHS rewrites to RHS always exported compile with -O no termination / semantic equivalence checks

Slide 17

Slide 17 text

Rewrite rules {-# RULES "whups" forall x y. f x y = f y x #-}

Slide 18

Slide 18 text

Rewrite rules {-# RULES "rev-involutive" forall xs. reverse (reverse xs) = xs #-}

Slide 19

Slide 19 text

Rewrite rules - fusion data Stream a where Stream :: (s -> Step s a) -> s -> Stream a data Step s a = Yield a s | Skip s | Done map :: (a -> b) -> [a] -> [b] map f = unstream . map . stream -- http://code.haskell.org/~dons/papers/icfp088-coutts.pdf

Slide 20

Slide 20 text

Rewrite rules - fusion map g . map f = unstream . map g . stream . unstream . map f . stream

Slide 21

Slide 21 text

Rewrite rules - fusion map g . map f = unstream . map g . id . map f . stream

Slide 22

Slide 22 text

Rewrite rules - fusion map g . map f = unstream . map g . map f . stream

Slide 23

Slide 23 text

Compiler options -ddump-rule-firings -ddump-rule-rewrites -ddump-inlinings -ddump-simpl-iterations

Slide 24

Slide 24 text

concise Control.Lens.Cons.Extras.recons :: (Cons s1 s1 a a, Cons s2 s2 a a, AsEmpty s2) => Getter s1 s2 -- https://hackage.haskell.org/package/concise

Slide 25

Slide 25 text

fresnel sepBy :: Grammar s a -> Grammar s () -> Grammar s [a] sepByT :: Grammar T.Text a -> Grammar T.Text () -> Grammar T.Text [a] sepByT g sep = prism (\(as, s) -> T.intercalate (print sep ()) (print g <$> as) <> s) (preview (sepBy g sep)) {-# RULES "sepBy/T" sepBy = sepByT #-} -- https://github.com/frasertweedale/hs-fresnel

Slide 26

Slide 26 text

Recap The three Rs: reuse, refactoring, readability Rewrite rules: make generic functions go fast Other optimisations enabled by rewrite rules Phase control: make inliner and simplifier play nice

Slide 27

Slide 27 text

Except where otherwise noted this work is licensed under http://creativecommons.org/licenses/by/4.0/ https://speakerdeck.com/frasertweedale @hackuador