(<$>) :: (a -> b) -> Parser a -> Parser b f <$> Parser p = Parser $ (fmap . first) f . p type Printer a = a -> Doc (<$>) :: (a -> b) -> Printer a -> Printer b 10 / 46
(<$>) :: (a -> b) -> Parser a -> Parser b f <$> Parser p = Parser $ (fmap . first) f . p type Printer a = a -> Doc (<$>) :: (a -> b) -> Printer a -> Printer b Can you implement this? 10 / 46
both a → b and b → a We unify both with a partial Iso, where these functions can fail We defined IsoFunctor (from partial isos to printer/parsers) 14 / 46
-> f a -> f b class UnhelpfulIsoApplicative where (<*>) :: f (Iso a b) -> f a -> f b type Printer a = a -> Doc instance UnhelpfulIsoApplicative Printer where (<*>) :: (Iso a b -> Doc) -> (a -> Doc) -> b -> Doc (f <*> g) b = error "how do I shot web?" 15 / 46
f (a -> b) -> f a -> f b class ProductFunctor f where infixr 6 <*> (<*>) :: f a -> f b -> f (a, b) instance ProductFunctor Printer where Printer p <*> Printer q = Printer $ \(x, y) -> liftM2 (++) (p x) (q y) 16 / 46
an uninhabitable type. We use ProductFunctor, it has tuples instead of currying and associates right <*> mushes tuples together one way, and takes them apart the other 18 / 46
Parser (List a) parseMany p = const Nil <$> text "" <|> Cons <$> p printMany :: (a -> Doc) -> (List a -> Doc) printMany p list = case list of Nil -> text "" Cons x xs -> p x <> printMany p xs 21 / 46
munging data IsoFunctor: to “lift” theses isos into concrete printers or parsers ProductFunctor: to handle multiple fields and sequencing via tuples 23 / 46
munging data IsoFunctor: to “lift” theses isos into concrete printers or parsers ProductFunctor: to handle multiple fields and sequencing via tuples Syntax: to glue all these constraints together and add pure 23 / 46
:: a -> Maybe Value } newtype JsonParser a = JsonParser { runParser :: Value -> Maybe a } instance IsoFunctor JsonBuilder where (<$>) :: Iso a b -> JsonBuilder a -> JsonBuilder b i <$> JsonBuilder b = JsonBuilder $ unapply i >=> b instance IsoFunctor JsonParser where (<$>) :: Iso a b -> JsonParser a -> JsonParser b i <$> JsonParser p = JsonParser $ apply i <=< p 25 / 46
:: JsonBuilder a -> JsonBuilder b -> JsonBuilder (a JsonBuilder p <*> JsonBuilder q = JsonBuilder $ \(a,b) -> do a <- p a b <- q b merge a b where merge (Object a) (Object b) = Just . Object $ a union b merge a (Array b) = Just . Array $ V.cons a b merge x Null = Just x merge Null x = Just x merge _ _ = Nothing 26 / 46
:: JsonParser a -> JsonParser b -> JsonParser (a,b) JsonParser p <*> JsonParser q = JsonParser $ \v -> do let (a,b) | Array x <- v, Just y <- x !? 0 = (y, Array $ V.tail x) | Array _ <- v = (Null, Null) | otherwise = (v,v) liftM2 (,) (p a) (q b) 27 / 46
(<||>) :: JsonBuilder a -> JsonBuilder a -> JsonBuilder a JsonBuilder p <||> JsonBuilder q = JsonBuilder $ \a -> p a mplus q a instance Alternative JsonParser where (<||>) :: JsonParser a -> JsonParser a -> JsonParser a JsonParser p <||> JsonParser q = JsonParser $ \v -> p v mplus q v 28 / 46
JsonBuilder Just runSub (JsonBuilder a) (JsonBuilder b) = JsonBuilder $ a >=> b instance JsonSyntax JsonParser where value = JsonParser Just runSub (JsonParser a) (JsonParser b) = JsonParser $ a <=< b 30 / 46
most of the JSON manipulation we want, but it has these terrifying Prism things: type Prism s t a b = (Choice p, Applicative f) => p a (f b) -> p s (f t) type Prism s a = Prism s s a a 31 / 46
most of the JSON manipulation we want, but it has these terrifying Prism things: type Prism s t a b = (Choice p, Applicative f) => p a (f b) -> p s (f t) type Prism s a = Prism s s a a preview :: Prism a b -> a -> Maybe b review :: Prism a b -> b -> a 31 / 46
Bool demote _Bool :: Iso Value Bool value :: Syntax s => s Value (<$>) :: Iso Value Bool -> s Value -> s Bool jsonBool :: JsonSyntax s => s Bool jsonBool = demote _Bool <$> value 33 / 46
Bool demote _Bool :: Iso Value Bool value :: Syntax s => s Value (<$>) :: Iso Value Bool -> s Value -> s Bool jsonBool :: JsonSyntax s => s Bool jsonBool = demote _Bool <$> value jsonNumber :: JsonSyntax s => s Scientific jsonNumber = demote _Number <$> value jsonString :: JsonSyntax s => s Text jsonString = demote _String <$> value 33 / 46
s Value -> s v jsonField :: JsonSyntax s => Text --^ Key to look up -> s v --^ Sub printer/parser to run on that key -> s v jsonField k sub = runSub sub (keyIso <$> value) where keyIso = demote $ prism (\x -> Object [(k,x)]) (^? key k) 34 / 46
field, which is a -- blob of text looking like: -- -- "00.43" currency :: JsonSyntax s => s Scientific currency = demoteLR "currency" (prism f g) <$> value where f = String . LT.toStrict . LT.toLazyText . fmt g = readMaybe . ST.unpack . ST.filter (not . isSpace) <=< preview _String -- We render with arbitrary precision (Nothing) -- and standard decimal notation (Fixed) fmt = LT.formatScientificBuilder Fixed Nothing 43 / 46
be redundant and error prone. A reasonable middle ground is detailed in the paper by Tillmann Rendel and Klaus Ostermann. Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing. 45 / 46
be redundant and error prone. A reasonable middle ground is detailed in the paper by Tillmann Rendel and Klaus Ostermann. Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing. It’s quite straight-forward to implement a simple unified Printer/Parser 45 / 46
be redundant and error prone. A reasonable middle ground is detailed in the paper by Tillmann Rendel and Klaus Ostermann. Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing. It’s quite straight-forward to implement a simple unified Printer/Parser Existing implementations of this general idea are still relatively immature. 45 / 46
be redundant and error prone. A reasonable middle ground is detailed in the paper by Tillmann Rendel and Klaus Ostermann. Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing. It’s quite straight-forward to implement a simple unified Printer/Parser Existing implementations of this general idea are still relatively immature. People do seem to be interested in and working on this 45 / 46