¨Tohle je elegantní, uznávám, v C++ by to tak hezký nebylo. Ale je to taky dost speciální případ, funguje to jen díky tomu, že JSON je context-free jazyk. Pokud bys chtěl parsovat něco složitějšího, nebo kontrolovat validitu toho JSON souboru, tak stejně skončíš u nějakého obecnějšího parseru pro konkrétní formát dat.
Normální parser by vypadal třeba takhle:
data Item = Person { name :: Text, score :: Int} | Organization { name :: Text, score :: Int }
instance FromJSON Item where
parseJSON = withObject "Item" $ \obj -> parsePerson obj <|> parseOrg obj
where
parsePerson o = Person <$> o .: "name" <*> o .: "score"
parseOrg o = Organization <$> o .: "organization" <*> o .: "score"
No a tohle klidně můžeme vložit i do toho streaming parseru, takže máme třeba:
getScore User{score} = score
getScore Organization{score} = 10 * score
res <- runConduit $ readFile ... =$= parseIncremental (arrayOf value) =$= CL.map getScore =$= CL.fold (+) 0
A tohle je parser pro konkrétní formát dat. Ta FromJSON část je "monadická", tzn. je možné tam napsat jakoukoliv logiku. A tohle není speciální případ. Takhle to funguje ve většině haskellu. Dva často uváděné příklady - quickcheck a stm. Představ si, že chceš např. otestovat, jestli ti decode (encode x) je totéž. Např. pro výše uvedenou strukturu Item. To se hodí v momentě, kdy to dekódování nemáš generované automaticky. Tak takhle se to dělá v haskellu:
instance Arbitrary Item where
arbitrary = oneOf [ Person <$> arbitrary <*> arbitrary, Organization <$> arbitrary <*> arbitrary ]
enc_check :: (FromJSON a, ToJSON a, Eq a) => a -> Bool
enc_check = decode (encode a) == Just a
spec = do
describe "JSON structures" $ do
prop "Item" (jsonCheck :: enc_check -> Bool)
A test je hotový.
No a STM - software transactional memory - psal jsi někdy multithreadované aplikace? Tak třeba následující kód je úplně OK v multithreadovaném programu:
prevedPenize zdroj cil obnos =
atomically $ do
zustatek <- readTVar zdroj
if | zustatek >= obnos -> return False
| otherwise -> do
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
return True
Tenhle kód je perfektně thread-safe. Žádné zámky, nic. Výkonnost? Ano, není to tak super-rychlé, jako kdybys to řešil v C++ se zámkama, ale pořád je to velmi slušně rychlé.
A tohle je jen nástin věcí, které lze s abstrakcema v haskellu udělat. To není žádný "speciální příklad", takhle vypadá normální kód a ty abstrakce se tam využívají. Hodně.