Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Интерпретирование языков
с помощью Free-монад
https://twitter.com/ZhekaKozlov
Пример 1: Console I/O
echo ∷ IO ()
echo = do
s ← getLine
putStrLn s
Проблемы
Как убедиться, что echo вызывает
только функции работы с консолью?
Мы не можем «заглянуть внутрь» IO и
проконтрол...
Нежелательный эффект
echo ∷ IO ()
echo = do
s ← getLine
launchMissile
putStrLn s
launchMissile ∷ IO ()
launchMissile = …
Калькулятор
calculator ∷ IO ()
calculator = do
x ← getLine
y ← getLine
putStrLn (show (read x + read y))
Проблемы
Как протестировать calculator?
сalculator возвращает IO() – мы не
можем протестировать ()!
Легко допустить ошибку
calculator ∷ IO ()
calculator = do
x ← getLine
y ← getLine
putStrLn (show (read x + read x))
Решение?
В этой книге!
1994 год
1994 год
RIP
Шаблон Интепретатор
«… определяет грамматику простого
языка, представляет предложения на
этом языке и интерпретирует их»
Пример грамматики
expression ← literal | alternation |
sequence | repetition | '(' expression ')'
alternation ← expression...
Диаграмма классов
RegularExpression
interpret()
LiteralExpression
interpret()
literal
SequenceExpression
interpret()
expre...
Диаграмма классов
Expr
interpret()
Literal
interpret()
literal
And
interpret()
expression1
expression2
Many
interpret()
ex...
ADT!
data Expr =
Literal String |
And Expr Expr |
Or Expr Expr |
Many Expr
Интерпретатор
interpret ∷ String → Expr → (Bool, String)
interpret s (Literal lit)
| lit `isPrefixOf` s =
(True, drop (len...
raining & (dogs | cats)*
And
expression1
expression2
Literal
‘raining’
Or
expression1
expression2
Many
expression
Literal
...
raining & (dogs | cats)*
ex = And (Literal "raining")
(Many (Or (Literal "dogs")
(Literal "cats")))
raining & (dogs | cats)*
ex = And (Literal "raining")
(Many (Or (Literal "dogs")
(Literal "cats")))
> interpret "rainingdo...
Таким образом
• Шаблон Интерпретатор определяет грамматику:
data Expr = Literal String |
And Expr Expr |
Or Expr Expr |
Ma...
Console I/O
Можно ли применить шаблон
Интерпретатор для языка консольного
ввода-вывода?
Console I/O
Можно ли применить шаблон
Интерпретатор для языка консольного
ввода-вывода?
Можно
Диаграмма классов ADT
Command
PrintLine
String
ReadLine
data Command =
PrintLine String |
ReadLine
Диаграмма классов ADT
Command
PrintLine
String
Command
ReadLine
data Command =
PrintLine String Command |
ReadLine (String...
Диаграмма классов ADT
Command a
PrintLine
String
a
ReadLine
data Command a =
PrintLine String a |
ReadLine (String → a)
St...
echo
data Command a =
PrintLine String a |
ReadLine (String → a)
echo = ReadLine (s → PrintLine s ())
calculator
data Command a =
PrintLine String a |
ReadLine (String → a)
calculator =
ReadLine (x →
ReadLine (y →
PrintLine
...
Проблема
> :type echo
echo ∷ Command (Command ())
> :type calculator
calculator ∷ Command (Command (Command ()))
Проблема
> :type echo
echo ∷ Command (Command ())
> :type calculator
calculator ∷ Command (Command (Command ()))
Как напис...
Free
data Free f a =
Return a |
Free (f (Free f a))
echo
data Free f a =
Return a |
Free (f (Free f a))
echo =
Free (ReadLine (s →
Free (PrintLine s (Return ()))))
calculator
data Free f a =
Return a |
Free (f (Free f a))
calculator =
Free (ReadLine (x ->
Free (ReadLine (y ->
Free (Pri...
Типы
> :type echo
echo ∷ Free Command ()
> :type calculator
calculator ∷ Free Command ()
Громоздко
calculator =
Free (ReadLine (x ->
Free (ReadLine (y ->
Free (PrintLine
(show (read x + read y))
(Return ()))))))
Free f является монадой!
Утверждение: если f является функтором, то
Free f является монадой.
data Free f a = Return a |
Fr...
Free f является монадой!
Утверждение: если f является функтором, то
Free f является монадой.
data Free f a = Return a |
Fr...
Command является функтором
data Command a = PrintLine String a |
ReadLine (String → a)
deriving Functor
Command является функтором
data Command a = PrintLine String a |
ReadLine (String → a)
deriving Functor
type Console = Fre...
Command является функтором
data Command a = PrintLine String a |
ReadLine (String → a)
deriving Functor
type Console = Fre...
echo
echo ∷ Console ()
echo = do
s ← readLine
printLine s
echo
echo ∷ Console ()
echo = do
s ← readLine
launchMissile
printLine s
Couldn't match type `IO' with `Console'
calculator
calculator ∷ Console ()
calculator = do
x ← readLine
y ← readLine
printLine (show (read x + read y))
Пишем интерпретатор
runConsole ∷ Console a → IO a
runConsole (Return a) = return a
runConsole (Free command) =
case comman...
Запускаем интерпретатор
> runConsole echo
Hello
Hello
> runConsole calculator
3
4
7
2-й интерпретатор (для теста)
Console a
runConsole testConsole
Тест
testConsole ∷
Console a → [String] → [String]
testConsole (Return _) _ = []
testConsole (Free command) inputs =
case ...
Запускаем тест
> :m + Test.QuickCheck
> quickCheck (s → testConsole echo
[s] == [s])
+++ OK, passed 100 tests
Запускаем тест
> quickCheck (x y → testConsole
calculator [show x, show y] == [show
(x + y)])
+++ OK, passed 100 tests
3-й интерпретатор (вывод в файл)
Console a
runConsole testConsole
runConsoleFile
Таким образом
• Шаблон Интепретатор определяет грамматику:
data Command a = PrintLine String a |
ReadLine (String → a)
der...
Пример 2: Key-Value Store
data KVSCommand a =
Put String String a |
Get String (String → a) |
Delete String a
deriving Fun...
Пример 2: Key-Value Store
put ∷ String → String → KVS ()
put k v = Free (Put k v (Return ()))
get ∷ String → KVS String
ge...
Пример 2: Key-Value Store
put ∷ String → String → KVS ()
put k v = Free (Put k v (Return ()))
get ∷ String → KVS String
ge...
Интерпретатор KVS (NoSQL)
runNoSQL ∷ KVS a → Connection → IO a
runNoSQL = …
Интерпретатор KVS (NoSQL)
runNoSQL ∷ KVS a → Connection → IO a
runNoSQL = …
kvs ∷ KVS String
kvs = do put "firstName" "Joh...
Интерпретатор KVS (Map)
import Data.Map
runMap ∷ KVS a → Map String String →
Map String String
runMap (Return _) map = map...
Эффективный интерпретатор
do put "key1" "value1"
put "key2" "value2"
put "key3" "value3"
"key1" ,"value1"
"key2" ,"value2"...
Эффективный интерпретатор
do put "key1" "value1"
put "key2" "value2"
put "key3" "value3"
"key1" ,"value1"
"key2" ,"value2"...
Пример 3: загрузка твитов
import Data.ByteString
data Tweet = Tweet {
id ∷ Integer,
userName ∷ String,
text ∷ String } der...
Twitter API
fetchTweets ∷ String → Int → IO [Tweet]
fetchTweets "odersky" _ = do
print ("Fetching tweets for @odersky")
re...
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int →
IO [(Tweet, ByteString)]
fetchTweetsWithPhotos name limi...
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int →
IO [(Tweet, ByteString)]
fetchTweetsWithPhotos name limi...
Хотим достать твиты с фото
fetchTweetsWithPhotos ∷ String → Int →
IO [(Tweet, ByteString)]
fetchTweetsWithPhotos name limi...
Решение
data TwitterCommand a =
GetTweets String Int ([Tweet] → a) |
GetUser String (User → a)
deriving Functor
type Twitt...
Решение
data TwitterCommand a =
GetTweets String Int ([Tweet] → a) |
GetUser String (User → a)
deriving Functor
type Twitt...
Кэширующий интерпретатор
runTwitter ∷ Twitter a → Map String User → IO a
runTwitter (Return a) _ = return a
runTwitter (Fr...
Хотим достать твиты с фото
getTweetsWithPhotos ∷ String → Int →
Twitter [(Tweet, ByteString)]
getTweetsWithPhotos name lim...
Ещё
При желании можно написать интерпретатор,
который распараллеливает запросы.
Существующие библиотеки
Haskell:
• https://github.com/facebook/Haxl
• http://hackage.haskell.org/package/CouchDB
Scala:
• ...
Вопросы?
Prochain SlideShare
Chargement dans…5
×

2

Partager

Télécharger pour lire hors ligne

Интерпретирование языков с помощью Free-монад

Télécharger pour lire hors ligne

Применение Free-монад для интерпретирования языков. Видео с доклада: http://www.youtube.com/watch?v=1Xa3sXj5Xfg

Livres associés

Gratuit avec un essai de 30 jours de Scribd

Tout voir

Интерпретирование языков с помощью Free-монад

  1. 1. Интерпретирование языков с помощью Free-монад https://twitter.com/ZhekaKozlov
  2. 2. Пример 1: Console I/O echo ∷ IO () echo = do s ← getLine putStrLn s
  3. 3. Проблемы Как убедиться, что echo вызывает только функции работы с консолью? Мы не можем «заглянуть внутрь» IO и проконтролировать, что не происходят нежелательные эффекты!
  4. 4. Нежелательный эффект echo ∷ IO () echo = do s ← getLine launchMissile putStrLn s launchMissile ∷ IO () launchMissile = …
  5. 5. Калькулятор calculator ∷ IO () calculator = do x ← getLine y ← getLine putStrLn (show (read x + read y))
  6. 6. Проблемы Как протестировать calculator? сalculator возвращает IO() – мы не можем протестировать ()!
  7. 7. Легко допустить ошибку calculator ∷ IO () calculator = do x ← getLine y ← getLine putStrLn (show (read x + read x))
  8. 8. Решение? В этой книге!
  9. 9. 1994 год
  10. 10. 1994 год RIP
  11. 11. Шаблон Интепретатор «… определяет грамматику простого языка, представляет предложения на этом языке и интерпретирует их»
  12. 12. Пример грамматики expression ← literal | alternation | sequence | repetition | '(' expression ')' alternation ← expression '|' expression sequence ← expression '&' expression repetition ← expression '*' literal ← ['a'..'z']*
  13. 13. Диаграмма классов RegularExpression interpret() LiteralExpression interpret() literal SequenceExpression interpret() expression1 expression2 RepetitionExpression interpret() expression AlternateExpression interpret() expression1 expression2
  14. 14. Диаграмма классов Expr interpret() Literal interpret() literal And interpret() expression1 expression2 Many interpret() expression Or interpret() expression1 expression2
  15. 15. ADT! data Expr = Literal String | And Expr Expr | Or Expr Expr | Many Expr
  16. 16. Интерпретатор 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)
  17. 17. raining & (dogs | cats)* And expression1 expression2 Literal ‘raining’ Or expression1 expression2 Many expression Literal ‘dogs’ Literal ‘cats’
  18. 18. raining & (dogs | cats)* ex = And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats")))
  19. 19. raining & (dogs | cats)* ex = And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats"))) > interpret "rainingdogscats1" ex (True,"1") > interpret "abc" ex (False,"abc")
  20. 20. Таким образом • Шаблон Интерпретатор определяет грамматику: data Expr = Literal String | And Expr Expr | Or Expr Expr | Many Expr And (Literal "raining") (Many (Or (Literal "dogs") (Literal "cats"))) • … представляет предложения на этом языке: • … и интерпретирует их: interpret = …
  21. 21. Console I/O Можно ли применить шаблон Интерпретатор для языка консольного ввода-вывода?
  22. 22. Console I/O Можно ли применить шаблон Интерпретатор для языка консольного ввода-вывода? Можно
  23. 23. Диаграмма классов ADT Command PrintLine String ReadLine data Command = PrintLine String | ReadLine
  24. 24. Диаграмма классов ADT Command PrintLine String Command ReadLine data Command = PrintLine String Command | ReadLine (String → Command) String → Command
  25. 25. Диаграмма классов ADT Command a PrintLine String a ReadLine data Command a = PrintLine String a | ReadLine (String → a) String → a
  26. 26. echo data Command a = PrintLine String a | ReadLine (String → a) echo = ReadLine (s → PrintLine s ())
  27. 27. calculator data Command a = PrintLine String a | ReadLine (String → a) calculator = ReadLine (x → ReadLine (y → PrintLine (show (read x + read y)) ()))
  28. 28. Проблема > :type echo echo ∷ Command (Command ()) > :type calculator calculator ∷ Command (Command (Command ()))
  29. 29. Проблема > :type echo echo ∷ Command (Command ()) > :type calculator calculator ∷ Command (Command (Command ())) Как написать интерпретатор для Command (Command (Command (…)))?
  30. 30. Free data Free f a = Return a | Free (f (Free f a))
  31. 31. echo data Free f a = Return a | Free (f (Free f a)) echo = Free (ReadLine (s → Free (PrintLine s (Return ()))))
  32. 32. 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 ()))))))
  33. 33. Типы > :type echo echo ∷ Free Command () > :type calculator calculator ∷ Free Command ()
  34. 34. Громоздко calculator = Free (ReadLine (x -> Free (ReadLine (y -> Free (PrintLine (show (read x + read y)) (Return ()))))))
  35. 35. Free f является монадой! Утверждение: если f является функтором, то Free f является монадой. data Free f a = Return a | Free (f (Free f a))
  36. 36. 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)
  37. 37. Command является функтором data Command a = PrintLine String a | ReadLine (String → a) deriving Functor
  38. 38. Command является функтором data Command a = PrintLine String a | ReadLine (String → a) deriving Functor type Console = Free Command
  39. 39. 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))
  40. 40. echo echo ∷ Console () echo = do s ← readLine printLine s
  41. 41. echo echo ∷ Console () echo = do s ← readLine launchMissile printLine s Couldn't match type `IO' with `Console'
  42. 42. calculator calculator ∷ Console () calculator = do x ← readLine y ← readLine printLine (show (read x + read y))
  43. 43. Пишем интерпретатор 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)
  44. 44. Запускаем интерпретатор > runConsole echo Hello Hello > runConsole calculator 3 4 7
  45. 45. 2-й интерпретатор (для теста) Console a runConsole testConsole
  46. 46. Тест 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)
  47. 47. Запускаем тест > :m + Test.QuickCheck > quickCheck (s → testConsole echo [s] == [s]) +++ OK, passed 100 tests
  48. 48. Запускаем тест > quickCheck (x y → testConsole calculator [show x, show y] == [show (x + y)]) +++ OK, passed 100 tests
  49. 49. 3-й интерпретатор (вывод в файл) Console a runConsole testConsole runConsoleFile
  50. 50. Таким образом • Шаблон Интепретатор определяет грамматику: data Command a = PrintLine String a | ReadLine (String → a) deriving Functor do s ← readLine printLine s • … представляет предложения на этом языке: • … и интерпретирует их: runConsole = … testConsole = …
  51. 51. Пример 2: Key-Value Store data KVSCommand a = Put String String a | Get String (String → a) | Delete String a deriving Functor type KVS = Free KVSCommand
  52. 52. Пример 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 ()))
  53. 53. Пример 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)
  54. 54. Интерпретатор KVS (NoSQL) runNoSQL ∷ KVS a → Connection → IO a runNoSQL = …
  55. 55. Интерпретатор 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
  56. 56. Интерпретатор 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)
  57. 57. Эффективный интерпретатор do put "key1" "value1" put "key2" "value2" put "key3" "value3" "key1" ,"value1" "key2" ,"value2" "key3" ,"value3"
  58. 58. Эффективный интерпретатор do put "key1" "value1" put "key2" "value2" put "key3" "value3" "key1" ,"value1" "key2" ,"value2" "key3" ,"value3" ["key1" ,"value1", "key2" ,"value2", "key3" ,"value3"]
  59. 59. Пример 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
  60. 60. 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])
  61. 61. Хотим достать твиты с фото 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))
  62. 62. Хотим достать твиты с фото 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"
  63. 63. Хотим достать твиты с фото 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 запроса!
  64. 64. Решение data TwitterCommand a = GetTweets String Int ([Tweet] → a) | GetUser String (User → a) deriving Functor type Twitter = Free TwitterCommand
  65. 65. Решение 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)
  66. 66. Кэширующий интерпретатор 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) Кэш фото
  67. 67. Хотим достать твиты с фото 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 запрос
  68. 68. Ещё При желании можно написать интерпретатор, который распараллеливает запросы.
  69. 69. Существующие библиотеки Haskell: • https://github.com/facebook/Haxl • http://hackage.haskell.org/package/CouchDB Scala: • Twitter Stitch (Scala, not open sourced) • https://github.com/MonsantoCo/stoop
  70. 70. Вопросы?
  • capkidd

    Aug. 16, 2016
  • StasBinko

    Dec. 24, 2015

Применение Free-монад для интерпретирования языков. Видео с доклада: http://www.youtube.com/watch?v=1Xa3sXj5Xfg

Vues

Nombre de vues

669

Sur Slideshare

0

À partir des intégrations

0

Nombre d'intégrations

18

Actions

Téléchargements

3

Partages

0

Commentaires

0

Mentions J'aime

2

×