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

Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05.2015

Embed Size (px)

Citation preview

Линзы

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

Александр Гранин

[email protected]

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 и setview (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 ===> [Presentation]

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

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

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

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

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

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

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

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

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

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

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

Тысячи их...

Zen28

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

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

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

Вопросы?

Александр Гранин

[email protected]

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