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

39
Линзы Комбинаторная манипуляция данными Александр Гранин [email protected] om 1

Upload: alexander-granin

Post on 30-Jul-2015

48 views

Category:

Technology


0 download

TRANSCRIPT

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

Линзы

Комбинаторная манипуляция данными

Александр Гранин[email protected]

1

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

О себе

● Haskell, C++, немного C#● Организатор сообщества LambdaNsk● Доклады:

o Haskell. DevDay@2GISo ФП вчера и сегодня. TechTalks@NSUo Функционально-декларативный дизайн на С++o Идиоматичный ФП-код. LambdaNsk

2

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

● Очень кратко о ФП

● Линзы в Haskell

● Сценарии с использованием линз

● Линзы в других языках

План доклада

3

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

Функциональное программирование

● Функции высших порядков● Лямбды● Замыкания● Иммутабельность● Рекурсия

4

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

Функциональное программирование

● Функции высших порядков● Лямбды● Замыкания● Иммутабельность● Рекурсия● Математические абстракции

5

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

data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String }

CredentialsLogin

Password

6

Простой алгебраический тип данных

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

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 }

CredentialsLogin

Password

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

data Credentials = Credentials { credentialsLogin :: String, credentialsPassword :: String }

data Person = Person { personName :: String, personSurname :: String, personCredentials :: Credentials }

8

Более сложный АТД

Person

CredentialsLogin

Password

CredentialsLogin

Password

Credentials

Name

Surname

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

updatePersonPassword person newPass = let credentials = personCredentials person newCreds = credentials { credentialsPassword = newPass } newPerson = person { personCredentials = newCreds } in newPerson

9

Изменение более сложного АТД

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

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

CredentialsLogin

Password

10

А что, если?..

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

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

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

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

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

person = subscriberPerson subscribernewSubscriber = subscriber { subscriberPerson = person }

credentials = personCredentials personnewPerson = person { personCredentials = credentials }

password = credentialsPassword credentialsnewCreds = credentials { credentialsPassword = password }

b = getB aa = setB b a

13

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

getPerson subscriber = subscriberPerson subscribersetPerson subscriber person = subscriber { subscriberPerson = person }

getCredentials person = personCredentials personsetCredentials person creds = person { personCredentials = creds }

getPassword creds = credentialsPassword credssetPassword creds pass = creds { credentialsPassword = pass }

b = getB aa = setB b a

14

getter :: A -> B

setter :: A -> B -> A

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

Линза = Геттер + Сеттер

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

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

Простые операторы view и setview (getter, _) parent = getter parentset (_, setter) parent value = setter parent value

> let myCredentials = Credentials "login" "pass"

> view passwordLens myCredentials"pass"

> set passwordLens myCredentials "qwerty"Credentials "login" "qwerty"

16

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

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

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

(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Композиция!

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

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

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

Линзы в Haskell: библиотека lensАвтор - Edward Kmett

20

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

data ContactType = VK | FB | Twitter | Emaildata Skill = Cpp | CSharp | Haskell | Java | FSharp | Python

data Person = Person { _name :: String, _surname :: String, _contacts :: [(ContactType, String)], _skills :: [Skill]}

АТД Person

PersonNameSurname

Contacts

Skills

21

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

type Presentation = Stringdata Attendee =

Speaker { _person :: Person, _visited :: [Presentation],

_presentationTitle :: String } | Attendee { _person :: Person, _visited :: [Presentation] }

АТД Attendee

Attendee

PersonNameSurname

Contacts

Skills

22

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

-- Template Haskell rocks:makeLenses ''PersonmakeLenses ''Attendee

Создание линз

_name name Person ===> String

_surname surname Person ===> String

_contacts contacts Person ===>Contacts

_person person Attendee ===> Person

_visited visited Attendee ===> [Presentation]

23

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

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

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

> peteAttendee ^. person.skills[Java,Python]

> itoList (peteAttendee ^. person.skills)[(0,Java),(1,Python)]

> (itoList (peteAttendee ^. person.skills)) ^.. ix 1[(1,Python)]

Операторы itoList, (^..) и ix

25

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

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

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

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

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

#%%= **= //= //~ <<%@= <<.|.= <<^~ %= ...

#%%~ **~ <-= <#%= <<%@~ <<.|.~ <<||= %@= .=

#%= *= <-~ <#%~ <<%~ <<.~ <<||~ %@~ .>

#%~ *~ <. <#= <<&&= <<//= <<~ %~ .@=

#= += <.&.= <#~ <<&&~ <<//~ <>= & .@~

#~ +~ <.&.~ <%= <<**= <</>= <>~ &&= .|.=

%%= -= <.= <%@= <<**~ <</>~ <?= &&~ .|.~

%%@= -~ <.> <%@~ <<*= <<<.>= <?~ &~ .~

%%@~ .&.= <.>= <%~ <<*~ <<<.>~ <^= <.|.= <<-=

%%~ .&.~ <.>~ <&&= <<+= <<</>= <^^= <.|.~ <<-~

Тысячи их...

Zen28

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

Сценарии с использованием линз

29

Для тех, кому еще мало

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

data Conference = Conference {

_attendees :: [Attendee],

_currentPresentation :: Presentation, _conferenceTweets ::

[(Presentation, Person, String)] }

АТД Conference

Attendee

Person

Conference

currentPresentation

conferenceTweets

30

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

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

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

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

Сценарий “Конференция”

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

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

Сценарий “Твит”

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

beginPresentation :: Presentation -> State Conference ()beginPresentation title = currentPresentation .= title

34

Сценарий “Начать доклад”

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

setListeners :: [Person] -> State Conference ()setListeners persons = setVisited (attendees . onlyListeners persons)

onlyListeners :: Persons -> Traversal' [Attendee] AttendeeonlyListeners persons

= traversed . filtered (\att -> (att ^. person) `elem` persons)

35

Сценарий “Задать слушателей”

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

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

Сценарий “Конференция”

λ!

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

Линзы в других языках● Haskell

○ Edward Kmett, “Lenses, Folds, and Traversals”

● Scalaz○ Edward Kmett, “Lenses: A Functional Imperative”

● JavaScript● Clojure● C++ (!)● … your language

37

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

38

Спасибо за внимание!

Вопросы?

Александр Гранин[email protected]

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

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] AttendeeonlyListeners ls = traversed . filtered (\att -> att ^. person `elem` ls)

39