This document contains a presentation about lenses in Haskell and other functional programming languages. It begins with an introduction to the speaker and an overview of functional programming concepts like higher-order functions, lambdas, and immutability. It then demonstrates how to define algebraic data types and lenses in Haskell to manipulate and access data in a functional way. Examples are provided for using lenses to update nested data structures. The remainder of the presentation discusses using lenses to define scenarios like managing attendees at a conference and adding tweets. It concludes by briefly mentioning lenses in other languages like Scala, JavaScript, and C++.
2. О себе
● Haskell, C++, немного C#
● Организатор сообщества LambdaNsk
● Доклады:
o Haskell. DevDay@2GIS
o ФП вчера и сегодня. TechTalks@NSU
o Функционально-декларативный дизайн на С++
o Идиоматичный ФП-код. LambdaNsk
2
3. ● Очень кратко о ФП
● Линзы в Haskell
● Сценарии с использованием линз
● Линзы в других языках
План доклада
3
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
Композиция!
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
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