![Page 1: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/1.jpg)
Интерпретирование языков с помощью Free-монад
https://twitter.com/ZhekaKozlov
![Page 2: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/2.jpg)
Пример 1: Console I/O
echo ∷ IO () echo = do s ← getLine putStrLn s
![Page 3: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/3.jpg)
Проблемы
Как убедиться, что echo вызывает только функции работы с консолью?
Мы не можем «заглянуть внутрь» IO и
проконтролировать, что не происходят нежелательные эффекты!
![Page 4: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/4.jpg)
Нежелательный эффект
echo ∷ IO () echo = do s ← getLine launchMissile putStrLn s launchMissile ∷ IO () launchMissile = …
![Page 5: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/5.jpg)
Калькулятор
calculator ∷ IO () calculator = do x ← getLine y ← getLine putStrLn (show (read x + read y))
![Page 6: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/6.jpg)
Проблемы
Как протестировать calculator? сalculator возвращает IO() – мы не
можем протестировать ()!
![Page 7: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/7.jpg)
Легко допустить ошибку
calculator ∷ IO () calculator = do x ← getLine y ← getLine putStrLn (show (read x + read x))
![Page 8: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/8.jpg)
Решение?
В этой книге!
![Page 9: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/9.jpg)
1994 год
![Page 10: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/10.jpg)
1994 год
RIP
![Page 11: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/11.jpg)
Шаблон Интепретатор
«… определяет грамматику простого языка, представляет предложения на этом языке и интерпретирует их»
![Page 12: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/12.jpg)
Пример грамматики
expression ← literal | alternation | sequence | repetition | '(' expression ')' alternation ← expression '|' expression sequence ← expression '&' expression
repetition ← expression '*'
literal ← ['a'..'z']*
![Page 13: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/13.jpg)
Диаграмма классов
RegularExpression
interpret()
LiteralExpression
interpret()
literal
SequenceExpression
interpret()
expression1
expression2
RepetitionExpression
interpret()
expression
AlternateExpression
interpret()
expression1
expression2
![Page 14: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/14.jpg)
Диаграмма классов
Expr
interpret()
Literal
interpret()
literal
And
interpret()
expression1
expression2
Many
interpret()
expression
Or
interpret()
expression1
expression2
![Page 15: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/15.jpg)
ADT!
data Expr = Literal String | And Expr Expr | Or Expr Expr | Many Expr
![Page 16: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/16.jpg)
Интерпретатор
interpret ∷ String → Expr → (Bool, String) interpret s (Literal lit) | lit `isPrefixOf` s = (True, drop (length lit) s) interpret s (And e1 e2) | (True, tail) ← interpret s e1 = interpret tail e2 interpret s (Or e1 e2) | (ok, tail) ← interpret s e1 = if ok then (True, tail) else interpret s e2 interpret s (Many e) | (ok, tail) ← interpret s e = if ok then interpret tail (Many e) else (True, s) interpret s _ = (False, s)
![Page 17: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/17.jpg)
raining & (dogs | cats)*
And
expression1 expression2
Literal
‘raining’
Or
expression1 expression2
Many
expression
Literal
‘dogs’
Literal
‘cats’
![Page 18: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/18.jpg)
raining & (dogs | cats)*
ex = And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats")))
![Page 19: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/19.jpg)
raining & (dogs | cats)*
ex = And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats"))) > interpret "rainingdogscats1" ex (True,"1") > interpret "abc" ex (False,"abc")
![Page 20: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/20.jpg)
Таким образом
• Шаблон Интерпретатор определяет грамматику:
data Expr = Literal String | And Expr Expr | Or Expr Expr | Many Expr
And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats")))
• … представляет предложения на этом языке:
• … и интерпретирует их:
interpret = …
![Page 21: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/21.jpg)
Console I/O
Можно ли применить шаблон Интерпретатор для языка консольного ввода-вывода?
![Page 22: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/22.jpg)
Console I/O
Можно ли применить шаблон Интерпретатор для языка консольного ввода-вывода?
Можно
![Page 23: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/23.jpg)
Диаграмма классов ADT
Command
PrintLine
String
ReadLine
data Command = PrintLine String | ReadLine
![Page 24: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/24.jpg)
Диаграмма классов ADT
Command
PrintLine
String Command
ReadLine
data Command = PrintLine String Command | ReadLine (String → Command)
String → Command
![Page 25: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/25.jpg)
Диаграмма классов ADT
Command a
PrintLine
String a
ReadLine
data Command a = PrintLine String a | ReadLine (String → a)
String → a
![Page 26: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/26.jpg)
echo
data Command a = PrintLine String a | ReadLine (String → a) echo = ReadLine (\s → PrintLine s ())
![Page 27: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/27.jpg)
calculator
data Command a = PrintLine String a | ReadLine (String → a) calculator = ReadLine (\x → ReadLine (\y → PrintLine (show (read x + read y)) ()))
![Page 28: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/28.jpg)
Проблема
> :type echo echo ∷ Command (Command ()) > :type calculator calculator ∷ Command (Command (Command ()))
![Page 29: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/29.jpg)
Проблема
> :type echo echo ∷ Command (Command ()) > :type calculator calculator ∷ Command (Command (Command ()))
Как написать интерпретатор для Command (Command (Command (…)))?
![Page 30: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/30.jpg)
Free
data Free f a = Return a | Free (f (Free f a))
![Page 31: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/31.jpg)
echo
data Free f a = Return a | Free (f (Free f a)) echo = Free (ReadLine (\s → Free (PrintLine s (Return ()))))
![Page 32: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/32.jpg)
calculator
data Free f a = Return a | Free (f (Free f a)) calculator = Free (ReadLine (\x -> Free (ReadLine (\y -> Free (PrintLine (show (read x + read y)) (Return ()))))))
![Page 33: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/33.jpg)
Типы
> :type echo echo ∷ Free Command () > :type calculator calculator ∷ Free Command ()
![Page 34: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/34.jpg)
Громоздко
calculator = Free (ReadLine (\x -> Free (ReadLine (\y -> Free (PrintLine (show (read x + read y)) (Return ()))))))
![Page 35: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/35.jpg)
Free f является монадой!
Утверждение: если f является функтором, то Free f является монадой.
data Free f a = Return a | Free (f (Free f a))
![Page 36: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/36.jpg)
Free f является монадой!
Утверждение: если f является функтором, то Free f является монадой.
data Free f a = Return a | Free (f (Free f a)) instance Functor f ⇒ Monad (Free f) where return = Return (Return a) >>= g = g a (Free f) >>= g = Free (fmap (>>= g) f)
![Page 37: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/37.jpg)
Command является функтором
data Command a = PrintLine String a | ReadLine (String → a) deriving Functor
![Page 38: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/38.jpg)
Command является функтором
data Command a = PrintLine String a | ReadLine (String → a) deriving Functor type Console = Free Command
![Page 39: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/39.jpg)
Command является функтором
data Command a = PrintLine String a | ReadLine (String → a) deriving Functor type Console = Free Command printLine ∷ String → Console () printLine s = Free (PrintLine s (Return ())) readLine ∷ Console String readLine = Free (ReadLine (\s → Return s))
![Page 40: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/40.jpg)
echo
echo ∷ Console () echo = do s ← readLine printLine s
![Page 41: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/41.jpg)
echo
echo ∷ Console () echo = do s ← readLine launchMissile printLine s
Couldn't match type `IO' with `Console'
![Page 42: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/42.jpg)
calculator
calculator ∷ Console () calculator = do x ← readLine y ← readLine printLine (show (read x + read y))
![Page 43: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/43.jpg)
Пишем интерпретатор
runConsole ∷ Console a → IO a runConsole (Return a) = return a runConsole (Free command) = case command of PrintLine s a → do putStrLn s runConsole a ReadLine after → do s ← getLine runConsole (after s)
![Page 44: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/44.jpg)
Запускаем интерпретатор
> runConsole echo Hello Hello > runConsole calculator 3 4 7
![Page 45: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/45.jpg)
2-й интерпретатор (для теста)
Console a
runConsole testConsole
![Page 46: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/46.jpg)
Тест
testConsole ∷ Console a → [String] → [String] testConsole (Return _) _ = [] testConsole (Free command) inputs = case command of PrintLine s a → s : testConsole a inputs ReadLine after → testConsole (after (head inputs)) (tail inputs)
![Page 47: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/47.jpg)
Запускаем тест
> :m + Test.QuickCheck > quickCheck (\s → testConsole echo [s] == [s]) +++ OK, passed 100 tests
![Page 48: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/48.jpg)
Запускаем тест
> quickCheck (\x y → testConsole calculator [show x, show y] == [show (x + y)]) +++ OK, passed 100 tests
![Page 49: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/49.jpg)
3-й интерпретатор (вывод в файл)
Console a
runConsole testConsole
runConsoleFile
![Page 50: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/50.jpg)
Таким образом
• Шаблон Интепретатор определяет грамматику:
data Command a = PrintLine String a | ReadLine (String → a) deriving Functor
do s ← readLine printLine s
• … представляет предложения на этом языке:
• … и интерпретирует их:
runConsole = … testConsole = …
![Page 51: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/51.jpg)
Пример 2: Key-Value Store
data KVSCommand a = Put String String a | Get String (String → a) | Delete String a deriving Functor type KVS = Free KVSCommand
![Page 52: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/52.jpg)
Пример 2: Key-Value Store
put ∷ String → String → KVS () put k v = Free (Put k v (Return ())) get ∷ String → KVS String get k = Free (Get k Return) del ∷ String → KVS () del k = Free (Delete k (Return ()))
![Page 53: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/53.jpg)
Пример 2: Key-Value Store
put ∷ String → String → KVS () put k v = Free (Put k v (Return ())) get ∷ String → KVS String get k = Free (Get k Return) del ∷ String → KVS () del k = Free (Delete k (Return ())) modify ∷ String → (String → String) → KVS () modify k f = do v ← get k put k (f v)
![Page 54: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/54.jpg)
Интерпретатор KVS (NoSQL)
runNoSQL ∷ KVS a → Connection → IO a runNoSQL = …
![Page 55: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/55.jpg)
Интерпретатор KVS (NoSQL)
runNoSQL ∷ KVS a → Connection → IO a runNoSQL = … kvs ∷ KVS String kvs = do put "firstName" "John" put "lastName" "Doe" modify "age" (show.(+1).read) get "salary" main = do conn ← getConnection runNoSQL kvs conn
![Page 56: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/56.jpg)
Интерпретатор KVS (Map)
import Data.Map runMap ∷ KVS a → Map String String → Map String String runMap (Return _) map = map runMap (Free f) map = case f of Put k v a → runMap a (insert k v map) Get k after → runMap (after (map ! k)) map Delete k a → runMap a (delete k map)
![Page 57: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/57.jpg)
Эффективный интерпретатор
do put "key1" "value1" put "key2" "value2" put "key3" "value3"
"key1" ,"value1"
"key2" ,"value2"
"key3" ,"value3"
![Page 58: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/58.jpg)
Эффективный интерпретатор
do put "key1" "value1" put "key2" "value2" put "key3" "value3"
"key1" ,"value1"
"key2" ,"value2"
"key3" ,"value3"
["key1" ,"value1", "key2" ,"value2", "key3" ,"value3"]
![Page 59: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/59.jpg)
Пример 3: загрузка твитов
import Data.ByteString data Tweet = Tweet { id ∷ Integer, userName ∷ String, text ∷ String } deriving Show data User = User { name ∷ String, photo ∷ ByteString } deriving Show fetchTweets ∷ String → Int → IO [Tweet] fetchUser ∷ String → IO User
![Page 60: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/60.jpg)
Twitter API
fetchTweets ∷ String → Int → IO [Tweet] fetchTweets "odersky" _ = do print ("Fetching tweets for @odersky") return [Tweet 1 "odersky" "Scala!", Tweet 2 "bos31337" "Haskell!", Tweet 3 "odersky" "Good morning"]
fetchUser ∷ String → IO User fetchUser name = do print ("Fetching user profile for @" ++ name) return $ case name of
"odersky" → User "odersky" (pack [1,2,3]) "bos31337" → User "bos31337" (pack [4,5])
![Page 61: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/61.jpg)
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int → IO [(Tweet, ByteString)] fetchTweetsWithPhotos name limit = do tweets ← fetchTweets name limit forM tweets (\t → do user ← fetchUser (userName t) return (t, image user))
![Page 62: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/62.jpg)
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int → IO [(Tweet, ByteString)] fetchTweetsWithPhotos name limit = do tweets ← fetchTweets name limit forM tweets (\t → do user ← fetchUser (userName t) return (t, image user)) > fetchTweetsWithPhotos "odersky" 10 "Fetching tweets for odersky" "Fetching user profile for odersky" "Fetching user profile for bos31337" "Fetching user profile for odersky"
![Page 63: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/63.jpg)
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int → IO [(Tweet, ByteString)] fetchTweetsWithPhotos name limit = do tweets ← fetchTweets name limit forM tweets (\t → do user ← fetchUser (userName t) return (t, image user)) > fetchTweetsWithPhotos "odersky" 10 "Fetching tweets for odersky" "Fetching user profile for odersky" "Fetching user profile for bos31337" "Fetching user profile for odersky"
2 запроса!
![Page 64: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/64.jpg)
Решение
data TwitterCommand a = GetTweets String Int ([Tweet] → a) | GetUser String (User → a) deriving Functor type Twitter = Free TwitterCommand
![Page 65: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/65.jpg)
Решение
data TwitterCommand a = GetTweets String Int ([Tweet] → a) | GetUser String (User → a) deriving Functor type Twitter = Free TwitterCommand getTweets ∷ String → Int → Twitter [Tweet] getTweets name limit = Free (GetTweets name limit Return) getUser ∷ String → Twitter User getUser name = Free (GetUser name Return)
![Page 66: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/66.jpg)
Кэширующий интерпретатор
runTwitter ∷ Twitter a → Map String User → IO a runTwitter (Return a) _ = return a runTwitter (Free f) cache = case f of GetTweets s limit after → do tweets ← fetchTweets s limit runTwitter (after tweets) cache GetUser name after → do user ← if member name cache then return (cache ! name) else fetchUser name runTwitter (after user) (insert name user cache)
Кэш фото
![Page 67: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/67.jpg)
Хотим достать твиты с фото
getTweetsWithPhotos ∷ String → Int → Twitter [(Tweet, ByteString)] getTweetsWithPhotos name limit = do tweets ← getTweets name limit forM tweets (\t → do user ← getUser (userName t) return (t, photo user)) > runTwitter (getTweetsWithPhotos "odersky" 10) empty "Fetching tweets for odersky" "Fetching user profile for odersky" "Fetching user profile for bos31337"
1 запрос
![Page 68: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/68.jpg)
Ещё
При желании можно написать интерпретатор, который распараллеливает запросы.
![Page 69: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/69.jpg)
Существующие библиотеки
Haskell: • https://github.com/facebook/Haxl • http://hackage.haskell.org/package/CouchDB Scala: • Twitter Stitch (Scala, not open sourced) • https://github.com/MonsantoCo/stoop
![Page 70: Интерпретирование языков с помощью Free-монад](https://reader034.vdocuments.net/reader034/viewer/2022042701/55abe6301a28abcc1f8b483d/html5/thumbnails/70.jpg)
Вопросы?