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.
Functional Microscope:
Lenses in С++
Alexander Granin
graninas@gmail.com
C++ Siberia, Novosibirsk
Me?
● C++, Haskell, C#
● C++ User Group Novosibirsk, 2014
● Talks, articles, FP evangelism...
● LambdaNsk - Novosibirsk FP...
struct Presentation
{
FP in C++?..
Functional Lenses
cpp_lenses library
};
4
FP in C++?..
С++ User Group Novosibirsk, 2014
FP concepts in C++
● Lambdas, closures, functions (almost pure)
● Immutability, POD-types
● Templates - pure functional la...
7
Functional Lenses
Lens 2 Lens 3Lens 1
8
Matryoshka
struct Account { Person person; };
struct Person { Address address; };
struct Address
{
std::string street;
i...
9
Mutable variables...
void setStreet(Account& account, const std::string& newStreet)
{
account.person.address.street = ne...
10
void setStreet(Account& account, const std::string& newStreet)
{
account.person.address.street = newStreet;
}
Mutable v...
11
Mutable state...
void Account::setStreet(const std::string& newStreet)
{
this->person.address.street = newStreet;
}
12
Mutable state...
void Account::setStreet(const std::string& newStreet)
{
this->person.address.street = newStreet;
}
● D...
13
Account setStreet(Account account, const std::string& newStreet)
{
account.person.address.street = newStreet;
return ac...
14
Account setStreet(Account account, const std::string& newStreet)
{
account.person.address.street = newStreet;
return ac...
Ok, Lenses!
auto lens = zoom(personLens, addressLens, streetLens);
auto newAccount = set(lens, oldAccount, std::string("Ne...
Ok, Lenses!
auto lens = zoom(personLens, addressLens, streetLens);
auto newAccount = set(lens, oldAccount, std::string("Ne...
Open matryoshka, pull out matryoshka...
Account account = {...};
Person person = getPerson(account);
Address address = get...
Person getPerson(const Account& account) {
return account.person;
}
Account setPerson(Account account, const Person& perso...
auto getPerson = [](const Account& account) {
return account.person;
};
auto setPerson = [](Account account, const Person&...
auto getPerson = [](const Account& account) {
return account.person;
};
auto setPerson = [](Account account, const Person&...
template <typename Value, typename Focus>
struct Lens {
std::function<Focus(Value)> getter;
std::function<Value(Value, Foc...
view
template <typename Value, typename Focus>
Focus view(const Lens<Value, Focus>& lens, const Value& value) {
return len...
set
template <typename Value, typename Focus>
Value set(const Lens<Value, Focus>& lens, const Value& value,
const Focus& n...
Lens composition is Lens too
Lens<Account, Person> personLens = { getPerson, setPerson };
Lens<Person, Address> addressLen...
Lens composition is Lens too
Lens<Account, Person> personLens = { getPerson, setPerson };
Lens<Person, Address> addressLen...
26
cpp_lens library
Manual lenses
template <typename Value, typename Focus>
Lens<Value, Focus> lens(const std::function<Focus(Value)>& getter,...
Autolenses
struct Account {
Person person;
std::string login;
std::string password;
};
#define MK_LENS(A, B, member) Lens<...
zoom
Lens<A, B> lens = aToB;
???<A, B, C> lens = zoom(aToB, bToC);
???<A, B, C, D> lens = zoom(aToB, bToC, cToD);
zoom (not generic) -> LensStack
Lens<A, B> lens = aToB;
LensStack<A, B, C> lens = zoom(aToB, bToC);
LensStack<A, B, C, D> ...
LensStack (not generic)
template <typename A, typename B, typename C = Id, typename D = Id>
struct LensStack {
Lens<A, B> ...
LensStack: Variadic Templates + magic
template<typename L, typename... Tail>
struct LensStack<L, Tail...> : LensStack<Tail...
Infix literal `to` combinator!
auto lens1 = addressL to houseL;
auto lens2 = personL to lens1;
auto lens3 = aL to bL to cL...
`to`: proxy + overloading + reroll stack
struct Proxy {...} proxy;
template <typename L1, typename L2>
LensStack<Lens<L1, ...
set
auto lens = personL() to addressL() to houseL();
Account account1 = {...};
Account account2 = set(lens, account1, 20);...
set, over
auto lens = personL() to addressL() to houseL();
Account account1 = {...};
Account account2 = set(lens, account1...
What about containers?
struct Car { std::string model; };
std::vector<Car> cars = { Car{"Ford Focus"}, Car{"Toyota Corolla...
toListOf *
struct Car { std::string model; };
std::vector<Car> cars = { Car{"Ford Focus"}, Car{"Toyota Corolla"} };
std::l...
traversed
struct Account { Person person; };
struct Person { std::vector<Car> cars; };
struct Car { std::string model;
int...
traversed + set
struct Account { Person person; };
struct Person { std::vector<Car> cars; };
struct Car { std::string mode...
traversed + over
struct Account { Person person; };
struct Person { std::vector<Car> cars; };
struct Car { std::string mod...
traversed + traversed!
struct Account { Person person; };
struct Person { std::vector<Car> cars; };
struct Car { std::stri...
cpp_lenses library
● Highly experimental
● Done: composing; set, view, over, traverse
● TODO: filter, traverse++, fold, pr...
● Complex structures processing
● Test data preparation
● Some XPath, LINQ analogue
● Functional approach
● Much better th...
Thank you!
Alexander Granin
graninas@gmail.com
Any questions?
C++ Siberia, Novosibirsk
Rerolling LensStack
template<typename L1, typename... Tail>
struct LS<L1, Tail...> : LS<Tail...>
{
template <typename Rero...
Upcoming SlideShare
Loading in …5
×

Functional microscope - Lenses in C++

889 views

Published on

Presentation for C++ Siberia 2015 about functional lenses in C++: how to do and how to use.

Published in: Software
  • Be the first to comment

Functional microscope - Lenses in C++

  1. 1. Functional Microscope: Lenses in С++ Alexander Granin graninas@gmail.com C++ Siberia, Novosibirsk
  2. 2. Me? ● C++, Haskell, C# ● C++ User Group Novosibirsk, 2014 ● Talks, articles, FP evangelism... ● LambdaNsk - Novosibirsk FP-community ● Kaspersky Lab
  3. 3. struct Presentation { FP in C++?.. Functional Lenses cpp_lenses library };
  4. 4. 4 FP in C++?..
  5. 5. С++ User Group Novosibirsk, 2014
  6. 6. FP concepts in C++ ● Lambdas, closures, functions (almost pure) ● Immutability, POD-types ● Templates - pure functional language ● FTL - Functional Template Library ● Initialization lists ● for_each(), recursion
  7. 7. 7 Functional Lenses Lens 2 Lens 3Lens 1
  8. 8. 8 Matryoshka struct Account { Person person; }; struct Person { Address address; }; struct Address { std::string street; int house; int flat; };
  9. 9. 9 Mutable variables... void setStreet(Account& account, const std::string& newStreet) { account.person.address.street = newStreet; }
  10. 10. 10 void setStreet(Account& account, const std::string& newStreet) { account.person.address.street = newStreet; } Mutable variables... ● Easy to break the client code ● Demetra law is violated ● Highly specific code ● Boilerplate
  11. 11. 11 Mutable state... void Account::setStreet(const std::string& newStreet) { this->person.address.street = newStreet; }
  12. 12. 12 Mutable state... void Account::setStreet(const std::string& newStreet) { this->person.address.street = newStreet; } ● Demetra law is violated ● Highly specific code ● Mixing of different layers ● SRP is violated ● Not a POD type
  13. 13. 13 Account setStreet(Account account, const std::string& newStreet) { account.person.address.street = newStreet; return account; } Immutable approach…
  14. 14. 14 Account setStreet(Account account, const std::string& newStreet) { account.person.address.street = newStreet; return account; } Immutable approach… Not so good. ● Easy to break the client code ● Demetra law is violated ● Highly specific code ● Boilerplate
  15. 15. Ok, Lenses! auto lens = zoom(personLens, addressLens, streetLens); auto newAccount = set(lens, oldAccount, std::string("New street")); ● “Focused” internal element of the structure ● Do something with the element from outside ● Hiding data structure realization ● Fully immutable, composable and reusable
  16. 16. Ok, Lenses! auto lens = zoom(personLens, addressLens, streetLens); auto newAccount = set(lens, oldAccount, std::string("New street")); So, how does this work?
  17. 17. Open matryoshka, pull out matryoshka... Account account = {...}; Person person = getPerson(account); Address address = getAddress(person); std::string street = getStreet(address); std::string newStreet = "Churchill's " + street; Address newAddress = setStreet(address, newStreet); Person newPerson = setAddress(person, newAddress); Account newAccount = setPerson(account, newPerson);
  18. 18. Person getPerson(const Account& account) { return account.person; } Account setPerson(Account account, const Person& person) { account.person = person; return account; } getA(), setA()
  19. 19. auto getPerson = [](const Account& account) { return account.person; }; auto setPerson = [](Account account, const Person& person) { account.person = person; return account; }; Getter, Setter
  20. 20. auto getPerson = [](const Account& account) { return account.person; }; auto setPerson = [](Account account, const Person& person) { account.person = person; return account; }; Getter, Setter std::function<Focus(Value)> getter; std::function<Value(Value, Focus)> setter;
  21. 21. template <typename Value, typename Focus> struct Lens { std::function<Focus(Value)> getter; std::function<Value(Value, Focus)> setter; }; Lens<Account, Person> personLens = { getPerson, setPerson }; Lens = Getter + Setter
  22. 22. view template <typename Value, typename Focus> Focus view(const Lens<Value, Focus>& lens, const Value& value) { return lens.getter(value); } Lens<Account, Person> personLens = { getPerson, setPerson }; Person person = view(personLens, someAccount);
  23. 23. set template <typename Value, typename Focus> Value set(const Lens<Value, Focus>& lens, const Value& value, const Focus& newFocus) { return l.setter(value, newFocus); } Lens<Account, Person> personLens = { getPerson, setPerson }; Person person = view(personLens, someAccount); Account newAccount = set(personLens, account, Person(”Santa”, ”Claus”));
  24. 24. Lens composition is Lens too Lens<Account, Person> personLens = { getPerson, setPerson }; Lens<Person, Address> addressLens = { getAddress, setAddress }; Lens<Address, std::string> streetLens = { getStreet, setStreet }; auto lens = zoom(personLens, addressLens, streetLens); // Magic zoom! Account newAccount = set(lens, someAccount, std::string(”Churchill's”));
  25. 25. Lens composition is Lens too Lens<Account, Person> personLens = { getPerson, setPerson }; Lens<Person, Address> addressLens = { getAddress, setAddress }; Lens<Address, std::string> streetLens = { getStreet, setStreet }; auto lens = zoom(personLens, addressLens, streetLens); // Magic zoom! // getPerson, getAddress, setStreet, setAddress, setPerson Account newAccount = set(lens, someAccount, std::string(”Churchill's”));
  26. 26. 26 cpp_lens library
  27. 27. Manual lenses template <typename Value, typename Focus> Lens<Value, Focus> lens(const std::function<Focus(Value)>& getter, const std::function<Value(Value, Focus)>& setter) { Lens<Value, Focus> l; l.getter = getter; l.setter = setter; return l; } auto personL = lens<Account, Person>( [](const Account& a) { return a.person; }, [](Account a, const Person& p) { a.person = p; return a; });
  28. 28. Autolenses struct Account { Person person; std::string login; std::string password; }; #define MK_LENS(A, B, member) Lens<A, B> member##L() { return lens<A, B> ( GETTER(A, member), SETTER(A, B, member)); } MK_LENS(Account, Person, person) // personL() MK_LENS(Account, std::string, login) // loginL() MK_LENS(Account, std::string, password) // passwordL()
  29. 29. zoom Lens<A, B> lens = aToB; ???<A, B, C> lens = zoom(aToB, bToC); ???<A, B, C, D> lens = zoom(aToB, bToC, cToD);
  30. 30. zoom (not generic) -> LensStack Lens<A, B> lens = aToB; LensStack<A, B, C> lens = zoom(aToB, bToC); LensStack<A, B, C, D> lens = zoom(aToB, bToC, cToD); template <typename A, typename B, typename C> LensStack<A, B, C> zoom(...) { … } template <typename A, typename B, typename C, typename D> LensStack<A, B, C, D> zoom(...) { ... }
  31. 31. LensStack (not generic) template <typename A, typename B, typename C = Id, typename D = Id> struct LensStack { Lens<A, B> lens1; Lens<B, C> lens2; Lens<C, D> lens3; }; LensStack<A, B, C> lens = zoom(aToB, bToC); // OK LensStack<A, B, C, D> lens = zoom(aToB, bToC, cToD); // OK LensStack<A, B, C, D, E> lens = zoom(aToB, bToC, cToD, dToE); // Ooops!
  32. 32. LensStack: Variadic Templates + magic template<typename L, typename... Tail> struct LensStack<L, Tail...> : LensStack<Tail...> { typedef LensStack<Tail...> base_type; LensStack(L lens, Tail... tail) : LensStack<Tail...>(tail...) , m_lens(lens) {} base_type& m_base = static_cast<base_type&>(*this); L m_lens; };
  33. 33. Infix literal `to` combinator! auto lens1 = addressL to houseL; auto lens2 = personL to lens1; auto lens3 = aL to bL to cL to dL to … to theLastOneLens; auto lens = (a to b) to c; // OK, left-associative auto lens = a to (b to c); // Error
  34. 34. `to`: proxy + overloading + reroll stack struct Proxy {...} proxy; template <typename L1, typename L2> LensStack<Lens<L1, L2>> operator<(const Lens<L1, L2>& lens, const Proxy&) { return LensStack<Lens<L1, L2>>(lens); } template <typename LS, typename L> typename LS::template reroll_type<L> operator>(const LS& stack, const L& lens) { return stack.reroll(lens); } // `Infix literal operator` trick #define to < proxy >
  35. 35. set auto lens = personL() to addressL() to houseL(); Account account1 = {...}; Account account2 = set(lens, account1, 20); // house == 20
  36. 36. set, over auto lens = personL() to addressL() to houseL(); Account account1 = {...}; Account account2 = set(lens, account1, 20); // house == 20 std::function<int(int)> modifier = [](int old) { return old + 6; }; Account account3 = over(lens, account2, modifier); // house == 26
  37. 37. What about containers? struct Car { std::string model; }; std::vector<Car> cars = { Car{"Ford Focus"}, Car{"Toyota Corolla"} };
  38. 38. toListOf * struct Car { std::string model; }; std::vector<Car> cars = { Car{"Ford Focus"}, Car{"Toyota Corolla"} }; std::list<std::string> result = toListOf(folded<Car>() to modelL(), cars); // result: {"Ford Focus", "Toyota Corolla"} * toListOf() and folded<T>() is a hack now, sorry...
  39. 39. traversed struct Account { Person person; }; struct Person { std::vector<Car> cars; }; struct Car { std::string model; int number; }; auto toCarL = personL() to carsL() to traversed<Car>();
  40. 40. traversed + set struct Account { Person person; }; struct Person { std::vector<Car> cars; }; struct Car { std::string model; int number; }; auto toCarL = personL() to carsL() to traversed<Car>(); Account newAccount1 = set(toCarL to modelL(), oldAccount, std::string(“Toyota”));
  41. 41. traversed + over struct Account { Person person; }; struct Person { std::vector<Car> cars; }; struct Car { std::string model; int number; }; auto toCarL = personL() to carsL() to traversed<Car>(); Account newAccount1 = set(toCarL to modelL(), oldAccount, std::string(“Toyota”)); std::function<std::string(std::string)> modifier = [](int old) { return old + 6; }; Account newAccount2 = over(toCarL to numberL(), newAccount1, modifier);
  42. 42. traversed + traversed! struct Account { Person person; }; struct Person { std::vector<Car> cars; }; struct Car { std::string model; int number; std::list<std::string> accessories; }; auto toAccessoryL = personL() to carsL() to traversed<Car>() to accessoriesL() to traversed<std::string>();
  43. 43. cpp_lenses library ● Highly experimental ● Done: composing; set, view, over, traverse ● TODO: filter, traverse++, fold, prisms, fusion… ● TODO: clean it, make it wise, short and robust ● github.com/graninas/cpp_lenses
  44. 44. ● Complex structures processing ● Test data preparation ● Some XPath, LINQ analogue ● Functional approach ● Much better than just <algorithm> ● ...Why not? Functional C++ is reality coming now Why lenses in C++?
  45. 45. Thank you! Alexander Granin graninas@gmail.com Any questions? C++ Siberia, Novosibirsk
  46. 46. Rerolling LensStack template<typename L1, typename... Tail> struct LS<L1, Tail...> : LS<Tail...> { template <typename Reroll, typename Lx> void reroll_(Reroll& rerolled, const Lx& lx) const { rerolled.m_lens = m_lens; base.reroll_(rerolled.base, lx); } template <typename Lx> LensStack<L1, Tail..., Lx> reroll(const Lx& lx) const { LensStack<L1, Tail..., Lx> rerolled; rerolled.m_lens = m_lens; base.reroll_(rerolled.base, lx); return rerolled; } } // Recursion base template <typename... Tail> struct LensStack { template <typename Reroll, typename Lx> void reroll_(Reroll& rerolled, const Lx& lx) { rerolled.m_lens = lx; } };

×