Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Линзы
Комбинаторная манипуляция данными
Александр Гранин
graninas@gmail.com
1
data Contacts = Contacts {
contactEMail :: String,
contactSkype :: String }
data Person = Person {
personName :: String,
p...
3
updateAccountEMail account newMail = let
CommonAccount person login = account
Person name surname contacts = person
Cont...
4
data Account =
CommonAccount {
accountPerson :: Person,
accountLogin :: String }
| PayedAccount {
accountPerson :: Perso...
5
updateAccountEMail account newMail = let
CommonAccount person login = account
Person name surname contacts = person
Cont...
6
updateAccountEMail account newMail = let
CommonAccount person login = account
Person name surname contacts = person
Cont...
7
updateAccountEMail account newMail = case account of
CommonAccount p l = CommonAccount (updateP p newMail) l
PayedAccoun...
8
updateAccountEMail account newMail = case account of
CommonAccount p l = CommonAccount (updateP p newMail) l
PayedAccoun...
9
updateAccountEMail account newMail = let
person = accountPerson account
contacts = personContacts person
newContacts = c...
getEMail contacts = contactEMail contacts
setEMail contacts mail = contacts { contactEMail = mail }
getContacts person = p...
Линза = Геттер + Сеттер
lens :: (getter :: A -> B,
setter :: A -> B -> A)
eMailLens :: (Contacts -> String,
Contacts -> St...
Комбинаторы view, set
view (getter, _) parent = getter parent
set (_, setter) parent value = setter parent value
> let con...
personLens = (getPerson, setPerson)
where
getPerson account = accountPerson account
setPerson account person = account {ac...
(getChild, setChild) . (getValue, setValue) = (getter, setter)
getter parent = getValue (getChild parent)
setter parent va...
Композиция линз - тоже линза
> let contacts = Contacts "someone@null.ptr" "not_a_skype_account"
> let person = Person “Som...
> let contacts = Contacts "someone@null.ptr" "not_a_skype_account"
> let person = Person “Some” “One” contacts
> let accou...
Линзы в Haskell: библиотека lens
Автор - Edward Kmett
17
18
Fold Setter
Getter Traversal
Lens
Review
Prism
Iso Equality
data Contacts = Contacts {
_mail :: String,
_skype :: String }
data Person = Person {
_name :: String,
_surname :: String,...
someoneContacts = Contacts "someone@null.ptr" "not_a_skype_account"
someOnePerson = Person "Some" "One" someoneContacts [C...
makeLenses ''Contacts
makeLenses ''Person
makeLenses ''Account
Создание линз (Template Haskell)
name Person ===> String
su...
Getters, Setters
22
Операторы view (^.), set (^~)
23
> let contacts’ = Contacts "someone@null.ptr" "not_a_skype_account"
> let person’ = Perso...
> let contacts’ = Contacts "someone@null.ptr" "not_a_skype_account"
> let person’ = Person “Some” “One” contacts’ [Cpp, Ha...
Folds
25
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell]
grutPerson = Person "IAm" "Grut" grutContact [FSharp]
>...
Prisms
35
> let result = Right grutPerson
> toListOf _Right result
[ Person {_name = "IAm", _surname = "Grut", _contacts = Contacts ...
Комбинирование
37
> let result = Right grutPerson
> over (_Right . surname) (map toUpper) result
Right [ Person {_name = "IAm", _surname = "...
> let result = Right accounts
> let mailLens = person . contacts . mail
> over (_Right . traverse . mailLens) (map toUpper...
#%%= **= //= //~ <<%@= <<.|.= <<^~ %= ...
#%%~ **~ <-= <#%= <<%@~ <<.|.~ <<||= %@= .=
#%= *= <-~ <#%~ <<%~ <<.~ <<||~ %@~ ...
Сложные структуры данных
41
Zen
42
Спасибо за внимание!
Вопросы?
Александр Гранин
graninas@gmail.com
beginPresentation :: Presentation -> State Conference ()
beginPresentation title = currentPresentation .= title
listeners ...
Upcoming SlideShare
Loading in …5
×

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

229 views

Published on

Линзы - концепция функционального программирования, позволяющая легко оперировать сколь угодно сложными структурами данных. Линзы решают две проблемы: снижают сложность обработки данных и предоставляют абстракцию над запросами к данным, делая код устойчивым к поломкам при изменении структуры данных.

Published in: Software
  • Be the first to comment

  • Be the first to like this

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

  1. 1. Линзы Комбинаторная манипуляция данными Александр Гранин graninas@gmail.com 1
  2. 2. data Contacts = Contacts { contactEMail :: String, contactSkype :: String } data Person = Person { personName :: String, personSurname :: String, personContacts :: Contacts } data Account = CommonAccount { accountPerson :: Person, accountLogin :: String } Person Account Contacts contactEMail contactSkype 2
  3. 3. 3 updateAccountEMail account newMail = let CommonAccount person login = account Person name surname contacts = person Contacts _ skype = contacts newContacts = Contacts newMail skype newPerson = Person name surname newContacts newAccount = CommonAccount newPerson login in newAccount
  4. 4. 4 data Account = CommonAccount { accountPerson :: Person, accountLogin :: String } | PayedAccount { accountPerson :: Person, accountLogin :: String, accountScores :: Int } data Contacts = Contacts { contactEMail :: String, contactSkype :: String } data Person = Person { personName :: String, personSurname :: String, personContacts :: Contacts }
  5. 5. 5 updateAccountEMail account newMail = let CommonAccount person login = account Person name surname contacts = person Contacts _ skype = contacts newContacts = Contacts newMail skype newPerson = Person name surname newContacts newAccount = CommonAccount newPerson login in newAccount ?
  6. 6. 6 updateAccountEMail account newMail = let CommonAccount person login = account Person name surname contacts = person Contacts _ skype = contacts newContacts = Contacts newMail skype newPerson = Person name surname newContacts newAccount = CommonAccount newPerson login in newAccount
  7. 7. 7 updateAccountEMail account newMail = case account of CommonAccount p l = CommonAccount (updateP p newMail) l PayedAccount p l s = PayedAccount (updateP p newMail) l s where updateP p mail = let Person name surname contacts = p Contacts _ skype = contacts newContacts = Contacts mail skype newPerson = Person name surname newContacts in newPerson
  8. 8. 8 updateAccountEMail account newMail = case account of CommonAccount p l = CommonAccount (updateP p newMail) l PayedAccount p l s = PayedAccount (updateP p newMail) l s where updateP p mail = let Person name surname contacts = p Contacts _ skype = contacts newContacts = Contacts mail skype newPerson = Person name surname newContacts in newPerson
  9. 9. 9 updateAccountEMail account newMail = let person = accountPerson account contacts = personContacts person newContacts = contacts { contactEMail = newMail } newPerson = person { personContacts = newContacts } newAccount = account { accountPerson = newPerson } in newAccount
  10. 10. getEMail contacts = contactEMail contacts setEMail contacts mail = contacts { contactEMail = mail } getContacts person = personContacts person setContacts person contacts = person { personContacts = contacts } getPerson account = accountPerson account setPerson account person = account { accountPerson = person } b = getB a a = setB b a 10 getter :: A -> B setter :: A -> B -> A
  11. 11. Линза = Геттер + Сеттер lens :: (getter :: A -> B, setter :: A -> B -> A) eMailLens :: (Contacts -> String, Contacts -> String -> Contacts) eMailLens = (getEMail, setEMail) where getEMail contacts = contactEMail contacts setEMail contacts mail = contacts {contactEMail = mail} 11
  12. 12. Комбинаторы view, set view (getter, _) parent = getter parent set (_, setter) parent value = setter parent value > let contacts = Contacts "someone@null.ptr" "not_a_skype_account" > view eMailLens contacts "someone@null.ptr" > set eMailLens contacts "dereference@null.ptr" Contacts "dereference@null.ptr" "not_a_skype_account" 12
  13. 13. personLens = (getPerson, setPerson) where getPerson account = accountPerson account setPerson account person = account {accountPerson = person} contactLens = (getContacts, setContacts) where getContacts person = personContacts person setContacts person contacts = person {personContacts = contacts} 13 Еще линзы
  14. 14. (getChild, setChild) . (getValue, setValue) = (getter, setter) getter parent = getValue (getChild parent) setter parent value = let oldChild = getChild parent newChild = setValue oldChild value in setChild parent newChild Композиция линз 14
  15. 15. Композиция линз - тоже линза > let contacts = Contacts "someone@null.ptr" "not_a_skype_account" > let person = Person “Some” “One” contacts > view (contactLens . eMailLens) person "someone@null.ptr" > set (contactLens . eMailLens) person "dereference@null.ptr" Person “Some” “One” (Contacts "dereference@null.ptr" "not_a_skype_account") 15
  16. 16. > let contacts = Contacts "someone@null.ptr" "not_a_skype_account" > let person = Person “Some” “One” contacts > let account = PayedAccount person “login” 0 > view (eMailLens . contactLens . personLens) account "someone@null.ptr" > set (eMailLens . contactLens . personLens) account "42@nm.ru" ………………………………………………………………………………… ………………………………………………………………………………... 16 Композиция линз - тоже линза
  17. 17. Линзы в Haskell: библиотека lens Автор - Edward Kmett 17
  18. 18. 18 Fold Setter Getter Traversal Lens Review Prism Iso Equality
  19. 19. data Contacts = Contacts { _mail :: String, _skype :: String } data Person = Person { _name :: String, _surname :: String, _contacts :: Contacts, _skills :: [Skill] } 19 data Account = CommonAccount { _person :: Person, _login :: String } | PayedAccount { _person :: Person, _login :: String, _scores :: Int } data Skill = Cpp | CSharp | Haskell | Java | FSharp | Python
  20. 20. someoneContacts = Contacts "someone@null.ptr" "not_a_skype_account" someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] someOneAccount = PayedAccount someOnePerson "login" 0 grutContact = Contacts "i@am.grut" "grut" grutPerson = Person "IAm" "Grut" grutContact [FSharp] grutAccount = CommonAccount grutPerson "bla" accounts = [someOneAccount, grutAccount] 20
  21. 21. makeLenses ''Contacts makeLenses ''Person makeLenses ''Account Создание линз (Template Haskell) name Person ===> String surname Person ===> String contacts Person ===> Contacts person Account ===> Person scores Account ===> Int ………. ……….. ………..... 21
  22. 22. Getters, Setters 22
  23. 23. Операторы view (^.), set (^~) 23 > let contacts’ = Contacts "someone@null.ptr" "not_a_skype_account" > let person’ = Person “Some” “One” contacts’ [Cpp, Haskell ] > let account’ = PayedAccount person’ “login” 0 > let mailLens = person . contacts . mail > view mailLens account’ "someone@null.ptr" > (set mailLens account’ "42@nm.ru") ^. mailLens "42@nm.ru"
  24. 24. > let contacts’ = Contacts "someone@null.ptr" "not_a_skype_account" > let person’ = Person “Some” “One” contacts’ [Cpp, Haskell ] > let account’ = PayedAccount person’ “login” 0 let mailLens = person . contacts . mail > (over mailLens (map toUpper) account') ^. mailLens "SOMEONE@NULL.PTR" Оператор over 24
  25. 25. Folds 25
  26. 26. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > view (folded . person . skills) accounts [Cpp,Haskell,FSharp] 26 Оператор folded
  27. 27. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > view (folded . person . surname) accounts “OneGrut” Оператор folded 27
  28. 28. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > view (folded . person . contacts) accounts No instance for (Data.Monoid.Monoid Contacts) arising from a use of ‘folded’ In the first argument of ‘(.)’, namely ‘folded’ In the first argument of ‘view’, namely ‘(folded . person . contacts)’ In the expression: view (folded . person . contacts) accounts 28 Оператор folded
  29. 29. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > toListOf (folded . person . contacts) accounts [ Contacts {_mail = "someone@null.ptr", _skype = "not_a_skype_account"} , Contacts {_mail = "i@am.grut", _skype = "grut"}] 29 Оператор toListOf
  30. 30. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > toListOf (folded . person . surname) accounts [“One”, “Grut”] 30 Оператор toListOf
  31. 31. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > let onlyGrut = filtered (acc -> acc ^. person.surname == "Grut") > view olnyGrut accounts No instance for (Data.Monoid.Monoid Account) arising from a use of ‘onlyGrut’ In the first argument of ‘view’, namely ‘onlyGrut’ In the expression: view onlyGrut accounts In an equation for ‘it’: it = view onlyGrut accounts 31 Оператор filtered
  32. 32. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > let onlyGrut = filtered (acc -> acc ^. person.surname == "Grut") > toListOf olnyGrut accounts Couldn't match type ‘[Account]’ with ‘Account’ Expected type: Getting (Data.Monoid.Endo [Account]) [Account] Account Actual type: Optic' (->) (Const (Data.Monoid.Endo [Account])) Account Account In the first argument of ‘toListOf’, namely ‘onlyGrut’ 32 Оператор filtered
  33. 33. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > let onlyGrut = filtered (acc -> acc ^. person.surname == "Grut") > toListOf (folded . olnyGrut) accounts [ CommonAccount {_person = Person {_name = "IAm", _surname = "Grut", _contacts = Contacts {_mail = "i@am.grut", _skype = "grut"}, _skills = [FSharp]}, _login = "bla"}] 33 Операторы filtered + folded
  34. 34. someOnePerson = Person "Some" "One" someoneContacts [Cpp, Haskell] grutPerson = Person "IAm" "Grut" grutContact [FSharp] > let withN = filtered (acc -> ‘m’ `elem` acc ^. person.name) > toListOf (folded . withN) accounts [ PayedAccount {_person = Person {_name = "Some", _surname = "One", _contacts = Contacts {_mail = "someone@null.ptr", _skype = "not_a_skype_account"}, _skills = [Cpp,Haskell]}, _login = "login", _scores = 0} , CommonAccount {_person = Person {_name = "IAm", _surname = "Grut", _contacts = Contacts {_mail = "i@am.grut", _skype = "grut"}, _skills = [FSharp]}, _login = "bla"}] 34 Еще пример filtered + folded
  35. 35. Prisms 35
  36. 36. > let result = Right grutPerson > toListOf _Right result [ Person {_name = "IAm", _surname = "Grut", _contacts = Contacts {_mail = "i@am.grut", _skype = "grut"}, _skills = [FSharp]} ] > toListOf _Left result [ ] 36 Операторы _Left, _Right
  37. 37. Комбинирование 37
  38. 38. > let result = Right grutPerson > over (_Right . surname) (map toUpper) result Right [ Person {_name = "IAm", _surname = "GRUT", _contacts = Contacts {_mail = "i@am.grut", _skype = "grut"}, _skills = [FSharp]} ] > over (_Left . surname) (map toUpper) result Right [ Person {_name = "IAm", _surname = "Grut", _contacts = Contacts {_mail = "i@am.grut", _skype = "grut"}, _skills = [FSharp]} ] 38 over + _Left, _Right
  39. 39. > let result = Right accounts > let mailLens = person . contacts . mail > over (_Right . traverse . mailLens) (map toUpper) result Right [ PayedAccount {_person = Person {_name = "Some", _surname = "One", _contacts = Contacts {_mail = "SOMEONE@NULL.PTR", _skype = "not_a_skype_account"}, _skills = [Cpp,Haskell]}, _login = "login", _scores = 0} , CommonAccount {_person = Person {_name = "IAm", _surname = "Grut", _contacts = Contacts {_mail = "I@AM.GRUT", _skype = "grut"}, _skills = [FSharp]}, _login = "bla"}] 39 over + traverse + _Right
  40. 40. #%%= **= //= //~ <<%@= <<.|.= <<^~ %= ... #%%~ **~ <-= <#%= <<%@~ <<.|.~ <<||= %@= .= #%= *= <-~ <#%~ <<%~ <<.~ <<||~ %@~ .> #%~ *~ <. <#= <<&&= <<//= <<~ %~ .@= #= += <.&.= <#~ <<&&~ <<//~ <>= & .@~ #~ +~ <.&.~ <%= <<**= <</>= <>~ &&= .|.= %%= -= <.= <%@= <<**~ <</>~ <?= &&~ .|.~ %%@= -~ <.> <%@~ <<*= <<<.>= <?~ &~ .~ %%@~ .&.= <.>= <%~ <<*~ <<<.>~ <^= <.|.= <<-= %%~ .&.~ <.>~ <&&= <<+= <<</>= <^^= <.|.~ <<-~ Тысячи операторов... 40
  41. 41. Сложные структуры данных 41 Zen
  42. 42. 42 Спасибо за внимание! Вопросы? Александр Гранин graninas@gmail.com
  43. 43. 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) 43

×