Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Линзы
Комбинаторная манипуляция данными
Александр Гранин
graninas@gmail.com
1
О себе
● Haskell, C++, немного C#
● Организатор сообщества LambdaNsk
● Доклады:
o Haskell. DevDay@2GIS
o ФП вчера и сегодня. TechTalks@NSU
o Функционально-декларативный дизайн на С++
o Идиоматичный ФП-код. LambdaNsk
2
● Очень кратко о ФП
● Линзы в Haskell
● Сценарии с использованием линз
● Линзы в других языках
План доклада
3
Функциональное программирование
● Функции высших порядков
● Лямбды
● Замыкания
● Иммутабельность
● Рекурсия
4
Функциональное программирование
● Функции высших порядков
● Лямбды
● Замыкания
● Иммутабельность
● Рекурсия
● Математические абстракции
5
data Credentials = Credentials {
credentialsLogin :: String,
credentialsPassword :: String }
Credentials
Login
Password
6
Простой алгебраический тип данных
updatePassword credentials newPassword = let
(Credentials login _) = credentials
in (Credentials login newPassword)
updatePassword2 credentials newPassword =
credentials { credentialsPassword = newPassword }
Изменение значения простого АТД
7
data Credentials = Credentials {
credentialsLogin :: String,
credentialsPassword :: String }
Credentials
Login
Password
data Credentials = Credentials {
credentialsLogin :: String,
credentialsPassword :: String }
data Person = Person {
personName :: String,
personSurname :: String,
personCredentials :: Credentials }
8
Более сложный АТД
Person
Credentials
Login
Password
Credentials
Login
Password
Credentials
Name
Surname
updatePersonPassword person newPass = let
credentials = personCredentials person
newCreds = credentials { credentialsPassword = newPass }
newPerson = person { personCredentials = newCreds }
in newPerson
9
Изменение более сложного АТД
data Credentials = Credentials {
credentialsLogin :: String,
credentialsPassword :: String }
data Person = Person {
personName :: String,
personSurname :: String,
personCredentials :: Credentials }
data Subscriber = Subscriber {
subscriberPerson :: Person,
subscriberTariffId :: Int }
Person
Subscriber
Credentials
Login
Password
10
А что, если?..
updateSubscriberPassword subscriber newPass = let
person = subscriberPerson subscriber
credentials = personCredentials person
newCreds = credentials { credentialsPassword = newPass }
newPerson = person { personCredentials = newCreds }
newSubscriber = subscriber { subscriberPerson = newPerson }
in newSubscriber
11
updateSubscriberPassword subscriber newPass = let
person = subscriberPerson subscriber
credentials = personCredentials person
newCreds = credentials { credentialsPassword = newPass }
newPerson = person { personCredentials = newCreds }
newSubscriber = subscriber { subscriberPerson = newPerson }
in newSubscriber
Getters
Setters
12
person = subscriberPerson subscriber
newSubscriber = subscriber { subscriberPerson = person }
credentials = personCredentials person
newPerson = person { personCredentials = credentials }
password = credentialsPassword credentials
newCreds = credentials { credentialsPassword = password }
b = getB a
a = setB b a
13
getPerson subscriber = subscriberPerson subscriber
setPerson subscriber person = subscriber { subscriberPerson = person }
getCredentials person = personCredentials person
setCredentials person creds = person { personCredentials = creds }
getPassword creds = credentialsPassword creds
setPassword creds pass = creds { credentialsPassword = pass }
b = getB a
a = setB b a
14
getter :: A -> B
setter :: A -> B -> A
Линза = Геттер + Сеттер
lens :: (getter :: A -> B,
setter :: A -> B -> A)
passwordLens :: (Credentials -> String,
Credentials -> String -> Credentials)
passwordLens = (getPass, setPass)
where
getPass creds = credentialsPassword creds
setPass creds newPass = creds {credentialsPassword = newPass}
15
Простые операторы view и set
view (getter, _) parent = getter parent
set (_, setter) parent value = setter parent value
> let myCredentials = Credentials "login" "pass"
> view passwordLens myCredentials
"pass"
> set passwordLens myCredentials "qwerty"
Credentials "login" "qwerty"
16
credentialsLens = (getCreds, setCreds)
where
getCreds person = personCredentials person
setCreds person newCreds = person {personCredentials = newCreds}
personLens = (getPerson, setPerson)
where
getPerson subscr = subscriberPerson subscr
setPerson subscr newPers = subscr {subscriberPerson = newPers}
Еще линзы
17
(getChild, setChild) . (getValue, setValue) = (getter, setter)
where
getter parent = getValue (getChild parent)
setter parent value = let
oldChild = getChild parent
newChild = setValue oldChild value
in setChild parent newChild
Линза . линза = линза
18
Композиция!
myCredentials = Credentials "login" "pass"
mePerson = Person "Alex" "Granin" myCredentials
> view (credentialsLens . passwordLens) mePerson
“pass”
> set (credentialsLens . passwordLens) mePerson “qwerty”
Person "Alex" "Granin" (Credentials "login" "qwerty")
Линза . линза = линза
19
Линзы в Haskell: библиотека lens
Автор - Edward Kmett
20
data ContactType = VK | FB | Twitter | Email
data Skill = Cpp | CSharp | Haskell | Java | FSharp | Python
data Person = Person {
_name :: String,
_surname :: String,
_contacts :: [(ContactType, String)],
_skills :: [Skill]
}
АТД Person
Person
Name
Surname
Contacts
Skills
21
type Presentation = String
data Attendee =
Speaker {
_person :: Person,
_visited :: [Presentation],
_presentationTitle :: String }
| Attendee {
_person :: Person,
_visited :: [Presentation] }
АТД Attendee
Attendee
Person
Name
Surname
Contacts
Skills
22
-- Template Haskell rocks:
makeLenses ''Person
makeLenses ''Attendee
Создание линз
_name name Person ===>
String
_surname surname Person ===>
String
_contacts contacts Person ===>
Contacts
_person person Attendee ===>
Person
_visited visited Attendee ===> 23
pete = Person "Pete" "Howard" [(FB, "pete")] [Java, Python]
peteAttendee = Attendee pete []
> _name (_person peteAttendee) -- Manual
“Pete”
> view (person.name) peteAttendee -- With lenses
“Pete”
> peteAttendee ^. (person.name) -- The same
“Pete”
Оператор view (^.)
24
> peteAttendee ^. person.skills
[Java,Python]
> itoList (peteAttendee ^. person.skills)
[(0,Java),(1,Python)]
> (itoList (peteAttendee ^. person.skills)) ^.. ix 1
[(1,Python)]
Операторы itoList, (^..) и ix
25
setAttendeeName att n = case att of
Speaker pers visit theme -> Speaker (setName pers) visit theme
Attendee pers visit -> Attendee (setName pers) visit
where
setName (Person _ s cts skls) = Person n s cts skls
setAttendeeName’ att n = set (person.name) "Jack" att
Оператор set
26
addContact att contact = case att of
Speaker pers visit theme -> Speaker (appendContact pers) visit theme
Attendee pers visit -> Attendee (appendContact pers) visit
where
appendContact (Person n s cts skls) = Person n s (contact : cts) skls
addContact' att contact =
over (person . contacts) (insert contact) att
Оператор over
27
#%%= **= //= //~ <<%@= <<.|.= <<^~ %= ...
#%%~ **~ <-= <#%= <<%@~ <<.|.~ <<||= %@= .=
#%= *= <-~ <#%~ <<%~ <<.~ <<||~ %@~ .>
#%~ *~ <. <#= <<&&= <<//= <<~ %~ .@=
#= += <.&.= <#~ <<&&~ <<//~ <>= & .@~
#~ +~ <.&.~ <%= <<**= <</>= <>~ &&= .|.=
%%= -= <.= <%@= <<**~ <</>~ <?= &&~ .|.~
%%@= -~ <.> <%@~ <<*= <<<.>= <?~ &~ .~
%%@~ .&.= <.>= <%~ <<*~ <<<.>~ <^= <.|.= <<-=
%%~ .&.~ <.>~ <&&= <<+= <<</>= <^^= <.|.~ <<-~
Тысячи их...
Zen 28
Сценарии с использованием линз
29
data Conference = Conference {
_attendees :: [Attendee],
_currentPresentation :: Presentation,
_conferenceTweets ::
[(Presentation, Person, String)]
}
АТД Conference
Attendee
Person
Conference
currentPresentation
conferenceTweets
30
pete = Person "Pete" "Howard" [facebook "pete"] [Java]
jakob = Person "Jakob" "Brown" [twitter "jab"] [Java, CSharp]
lana = Person "Lana" "Dell" [] [FSharp]
alex = Person "Alexander" "Granin" [vk "graninas"] [Haskell]
guru = Person "Real" "Guru" [] [Cpp, Java, CSharp, Haskell]
confAttendees = [ attendee pete, attendee jakob, attendee lana,
speaker alex "Lenses",
speaker guru “Multilanguage projects”]
conference = Conference confAttendees "" []
31
conferenceScenario :: State Conference ()
conferenceScenario = do
alex `tweeted` "Rush hour, sorry. #dev2dev"
beginPresentation "Multilanguage projects"
setListeners [pete, jakob, lana]
jakob `tweeted` "Great talk! #dev2dev"
lana `tweeted` "So technical. #dev2dev"
pete `tweeted` "#MLP 222 coming soon."
32
Сценарий “Конференция”
tweeted :: Person -> Msg -> State Conference ()
tweeted pers msg = if "#dev2dev" `isInfixOf` msg
then writeTweet pers msg
else return ()
writeTweet :: Person -> Msg -> State Conference ()
writeTweet pers msg = do
presentationName <- use currentPresentation
conferenceTweets %= insert (presentationName, pers, msg)
33
Сценарий “Твит”
beginPresentation :: Presentation -> State Conference ()
beginPresentation title = currentPresentation .= title
34
Сценарий “Начать доклад”
setListeners :: [Person] -> State Conference ()
setListeners persons = setVisited (attendees . onlyListeners persons)
onlyListeners :: Persons -> Traversal' [Attendee] Attendee
onlyListeners persons
= traversed . filtered (att -> (att ^. person) `elem` persons)
35
Сценарий “Задать слушателей”
conferenceScenario :: State Conference ()
conferenceScenario = do
alex `tweeted` "Rush hour, sorry. #conf"
beginPresentation "Multilanguage projects"
setListeners [pete, jakob, lana]
jakob `tweeted` "Great talk! #conf"
lana `tweeted` "So technical. #conf"
pete `tweeted` "#MLP 222 coming soon."
36
Сценарий “Конференция”
Линзы в других языках
● Haskell
○ Edward Kmett, “Lenses, Folds, and Traversals”
● Scalaz
○ Edward Kmett, “Lenses: A Functional Imperative”
● JavaScript
● Clojure
● C++ (!)
● … your language
37
38
Спасибо за внимание!
Вопросы?
Александр Гранин
graninas@gmail.com
beginPresentation :: Presentation -> State Conference ()
beginPresentation title = currentPresentation .= title
listeners :: [Person] -> State Conference ()
listeners perss = setVisited (attendees . onlyListeners perss)
where
setVisited atts = do
title <- use currentPresentation
atts.visited %= (insert title)
onlyListeners :: Persons -> Traversal' [Attendee] Attendee
onlyListeners ls = traversed . filtered (att -> att ^. person `elem` ls)
39

More Related Content

Линзы - комбинаторная манипуляция данными (Dev2Dev)

  • 2. О себе ● Haskell, C++, немного C# ● Организатор сообщества LambdaNsk ● Доклады: o Haskell. DevDay@2GIS o ФП вчера и сегодня. TechTalks@NSU o Функционально-декларативный дизайн на С++ o Идиоматичный ФП-код. LambdaNsk 2
  • 3. ● Очень кратко о ФП ● Линзы в Haskell ● Сценарии с использованием линз ● Линзы в других языках План доклада 3
  • 4. Функциональное программирование ● Функции высших порядков ● Лямбды ● Замыкания ● Иммутабельность ● Рекурсия 4
  • 5. Функциональное программирование ● Функции высших порядков ● Лямбды ● Замыкания ● Иммутабельность ● Рекурсия ● Математические абстракции 5
  • 6. data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String } Credentials Login Password 6 Простой алгебраический тип данных
  • 7. updatePassword credentials newPassword = let (Credentials login _) = credentials in (Credentials login newPassword) updatePassword2 credentials newPassword = credentials { credentialsPassword = newPassword } Изменение значения простого АТД 7 data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String } Credentials Login Password
  • 8. data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String } data Person = Person { personName :: String, personSurname :: String, personCredentials :: Credentials } 8 Более сложный АТД Person Credentials Login Password Credentials Login Password Credentials Name Surname
  • 9. updatePersonPassword person newPass = let credentials = personCredentials person newCreds = credentials { credentialsPassword = newPass } newPerson = person { personCredentials = newCreds } in newPerson 9 Изменение более сложного АТД
  • 10. data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String } data Person = Person { personName :: String, personSurname :: String, personCredentials :: Credentials } data Subscriber = Subscriber { subscriberPerson :: Person, subscriberTariffId :: Int } Person Subscriber Credentials Login Password 10 А что, если?..
  • 11. updateSubscriberPassword subscriber newPass = let person = subscriberPerson subscriber credentials = personCredentials person newCreds = credentials { credentialsPassword = newPass } newPerson = person { personCredentials = newCreds } newSubscriber = subscriber { subscriberPerson = newPerson } in newSubscriber 11
  • 12. updateSubscriberPassword subscriber newPass = let person = subscriberPerson subscriber credentials = personCredentials person newCreds = credentials { credentialsPassword = newPass } newPerson = person { personCredentials = newCreds } newSubscriber = subscriber { subscriberPerson = newPerson } in newSubscriber Getters Setters 12
  • 13. person = subscriberPerson subscriber newSubscriber = subscriber { subscriberPerson = person } credentials = personCredentials person newPerson = person { personCredentials = credentials } password = credentialsPassword credentials newCreds = credentials { credentialsPassword = password } b = getB a a = setB b a 13
  • 14. getPerson subscriber = subscriberPerson subscriber setPerson subscriber person = subscriber { subscriberPerson = person } getCredentials person = personCredentials person setCredentials person creds = person { personCredentials = creds } getPassword creds = credentialsPassword creds setPassword creds pass = creds { credentialsPassword = pass } b = getB a a = setB b a 14 getter :: A -> B setter :: A -> B -> A
  • 15. Линза = Геттер + Сеттер lens :: (getter :: A -> B, setter :: A -> B -> A) passwordLens :: (Credentials -> String, Credentials -> String -> Credentials) passwordLens = (getPass, setPass) where getPass creds = credentialsPassword creds setPass creds newPass = creds {credentialsPassword = newPass} 15
  • 16. Простые операторы view и set view (getter, _) parent = getter parent set (_, setter) parent value = setter parent value > let myCredentials = Credentials "login" "pass" > view passwordLens myCredentials "pass" > set passwordLens myCredentials "qwerty" Credentials "login" "qwerty" 16
  • 17. credentialsLens = (getCreds, setCreds) where getCreds person = personCredentials person setCreds person newCreds = person {personCredentials = newCreds} personLens = (getPerson, setPerson) where getPerson subscr = subscriberPerson subscr setPerson subscr newPers = subscr {subscriberPerson = newPers} Еще линзы 17
  • 18. (getChild, setChild) . (getValue, setValue) = (getter, setter) where getter parent = getValue (getChild parent) setter parent value = let oldChild = getChild parent newChild = setValue oldChild value in setChild parent newChild Линза . линза = линза 18 Композиция!
  • 19. myCredentials = Credentials "login" "pass" mePerson = Person "Alex" "Granin" myCredentials > view (credentialsLens . passwordLens) mePerson “pass” > set (credentialsLens . passwordLens) mePerson “qwerty” Person "Alex" "Granin" (Credentials "login" "qwerty") Линза . линза = линза 19
  • 20. Линзы в Haskell: библиотека lens Автор - Edward Kmett 20
  • 21. data ContactType = VK | FB | Twitter | Email data Skill = Cpp | CSharp | Haskell | Java | FSharp | Python data Person = Person { _name :: String, _surname :: String, _contacts :: [(ContactType, String)], _skills :: [Skill] } АТД Person Person Name Surname Contacts Skills 21
  • 22. type Presentation = String data Attendee = Speaker { _person :: Person, _visited :: [Presentation], _presentationTitle :: String } | Attendee { _person :: Person, _visited :: [Presentation] } АТД Attendee Attendee Person Name Surname Contacts Skills 22
  • 23. -- Template Haskell rocks: makeLenses ''Person makeLenses ''Attendee Создание линз _name name Person ===> String _surname surname Person ===> String _contacts contacts Person ===> Contacts _person person Attendee ===> Person _visited visited Attendee ===> 23
  • 24. pete = Person "Pete" "Howard" [(FB, "pete")] [Java, Python] peteAttendee = Attendee pete [] > _name (_person peteAttendee) -- Manual “Pete” > view (person.name) peteAttendee -- With lenses “Pete” > peteAttendee ^. (person.name) -- The same “Pete” Оператор view (^.) 24
  • 25. > peteAttendee ^. person.skills [Java,Python] > itoList (peteAttendee ^. person.skills) [(0,Java),(1,Python)] > (itoList (peteAttendee ^. person.skills)) ^.. ix 1 [(1,Python)] Операторы itoList, (^..) и ix 25
  • 26. setAttendeeName att n = case att of Speaker pers visit theme -> Speaker (setName pers) visit theme Attendee pers visit -> Attendee (setName pers) visit where setName (Person _ s cts skls) = Person n s cts skls setAttendeeName’ att n = set (person.name) "Jack" att Оператор set 26
  • 27. addContact att contact = case att of Speaker pers visit theme -> Speaker (appendContact pers) visit theme Attendee pers visit -> Attendee (appendContact pers) visit where appendContact (Person n s cts skls) = Person n s (contact : cts) skls addContact' att contact = over (person . contacts) (insert contact) att Оператор over 27
  • 28. #%%= **= //= //~ <<%@= <<.|.= <<^~ %= ... #%%~ **~ <-= <#%= <<%@~ <<.|.~ <<||= %@= .= #%= *= <-~ <#%~ <<%~ <<.~ <<||~ %@~ .> #%~ *~ <. <#= <<&&= <<//= <<~ %~ .@= #= += <.&.= <#~ <<&&~ <<//~ <>= & .@~ #~ +~ <.&.~ <%= <<**= <</>= <>~ &&= .|.= %%= -= <.= <%@= <<**~ <</>~ <?= &&~ .|.~ %%@= -~ <.> <%@~ <<*= <<<.>= <?~ &~ .~ %%@~ .&.= <.>= <%~ <<*~ <<<.>~ <^= <.|.= <<-= %%~ .&.~ <.>~ <&&= <<+= <<</>= <^^= <.|.~ <<-~ Тысячи их... Zen 28
  • 30. data Conference = Conference { _attendees :: [Attendee], _currentPresentation :: Presentation, _conferenceTweets :: [(Presentation, Person, String)] } АТД Conference Attendee Person Conference currentPresentation conferenceTweets 30
  • 31. pete = Person "Pete" "Howard" [facebook "pete"] [Java] jakob = Person "Jakob" "Brown" [twitter "jab"] [Java, CSharp] lana = Person "Lana" "Dell" [] [FSharp] alex = Person "Alexander" "Granin" [vk "graninas"] [Haskell] guru = Person "Real" "Guru" [] [Cpp, Java, CSharp, Haskell] confAttendees = [ attendee pete, attendee jakob, attendee lana, speaker alex "Lenses", speaker guru “Multilanguage projects”] conference = Conference confAttendees "" [] 31
  • 32. conferenceScenario :: State Conference () conferenceScenario = do alex `tweeted` "Rush hour, sorry. #dev2dev" beginPresentation "Multilanguage projects" setListeners [pete, jakob, lana] jakob `tweeted` "Great talk! #dev2dev" lana `tweeted` "So technical. #dev2dev" pete `tweeted` "#MLP 222 coming soon." 32 Сценарий “Конференция”
  • 33. tweeted :: Person -> Msg -> State Conference () tweeted pers msg = if "#dev2dev" `isInfixOf` msg then writeTweet pers msg else return () writeTweet :: Person -> Msg -> State Conference () writeTweet pers msg = do presentationName <- use currentPresentation conferenceTweets %= insert (presentationName, pers, msg) 33 Сценарий “Твит”
  • 34. beginPresentation :: Presentation -> State Conference () beginPresentation title = currentPresentation .= title 34 Сценарий “Начать доклад”
  • 35. setListeners :: [Person] -> State Conference () setListeners persons = setVisited (attendees . onlyListeners persons) onlyListeners :: Persons -> Traversal' [Attendee] Attendee onlyListeners persons = traversed . filtered (att -> (att ^. person) `elem` persons) 35 Сценарий “Задать слушателей”
  • 36. conferenceScenario :: State Conference () conferenceScenario = do alex `tweeted` "Rush hour, sorry. #conf" beginPresentation "Multilanguage projects" setListeners [pete, jakob, lana] jakob `tweeted` "Great talk! #conf" lana `tweeted` "So technical. #conf" pete `tweeted` "#MLP 222 coming soon." 36 Сценарий “Конференция”
  • 37. Линзы в других языках ● Haskell ○ Edward Kmett, “Lenses, Folds, and Traversals” ● Scalaz ○ Edward Kmett, “Lenses: A Functional Imperative” ● JavaScript ● Clojure ● C++ (!) ● … your language 37
  • 39. beginPresentation :: Presentation -> State Conference () beginPresentation title = currentPresentation .= title listeners :: [Person] -> State Conference () listeners perss = setVisited (attendees . onlyListeners perss) where setVisited atts = do title <- use currentPresentation atts.visited %= (insert title) onlyListeners :: Persons -> Traversal' [Attendee] Attendee onlyListeners ls = traversed . filtered (att -> att ^. person `elem` ls) 39