SlideShare ist ein Scribd-Unternehmen logo
1 von 28
Downloaden Sie, um offline zu lesen
If one cannot get one’s own way, one must bow to the inevitable...
Pseudo-dynamic
immutable records in C++
Anton Tcholakov
Software Engineer,
Pico Technology
About
❖ Software engineer
working on drivers at
Pico Technology
❖ Physics graduate with
experience in
experimental physics
❖ Amateur
photographer
About
❖ Software engineer
working on drivers at
Pico Technology
❖ Physics graduate with
experience in
experimental physics
❖ Amateur
photographer
About
❖ Software engineer
working on drivers at
Pico Technology
❖ Physics graduate with
experience in
experimental physics
❖ Amateur
photographer
Pseudo-dynamic immutable records
“If it walks like a duck and quacks like a duck then
it must be a duck.”
class Duck:
def fly(self):
print("Duck flying")
class Airplane:
def fly(self):
print("Airplane flying")
for thing in Duck(), Airplane():
thing.fly()
Pseudo-dynamic immutable records
immutable (adj.)
unchanging over time or unable to be changed
-module(lists).
-export(sum/1).
sum([], acc) -> acc;
sum([First|Rest], acc) -> sum(Rest, First + acc).
sum(List) -> sum(List, 0).
Pseudo-dynamic immutable records
composite data type (n.)
… composed from primitives and other composites
type Account =
{ AccountNumber : int
FirstName : string
LastName : string
Balance : float }
… in C++. Why?!
❖ Having different mental models for how to deconstruct
problems is incredibly valuable
❖ Functional techniques are very well-suited to
programming concurrent systems
❖ C++ is a very malleable programming language
❖ Intellectual curiosity…
Building on immer
https://github.com/arximboldi/immer
immutable, persistent data structures for C++
#include <immer/vector.hpp>
int main() {
const auto v0 = immer::vector<int>{};
const auto v1 = v0.push_back(13);
assert(v0.size() == 0 && v1.size() == 1 && v1[0] == 13);
const auto v2 = v1.set(0, 42);
assert(v1[0] == 13 && v2[0] == 42);
}
Structural sharing
https://hypirion.com/musings/understanding-persistent-vector-pt-1
aeternum
❖ Name is Latin for “forever”…
❖ Currently just a playground
❖ https://github.com/anton-pt/aeternum
atoms
❖ Literal types represented by C-style strings
❖ Name is hashed using CRC-32 at compile time
❖ Useful as “tags” for dynamic types and key-value pairs
constexpr aeternum::atom apples("apples");
constexpr aeternum::atom oranges("oranges");
std::cout << std::boolalpha << (apples == oranges);
// output: false
tagged_untyped and tagged<T>
❖ tagged<T> inherits from tagged_untyped
❖ Supports std::hash and operator== provided that T does
const auto four_apples = aeternum::make_tagged(apples, 4);
const auto four_oranges = aeternum::make_tagged(oranges, 4);
const auto five_apples = aeternum::make_tagged(apples, 5);
const auto four_more_oranges = aeternum::make_tagged(oranges, 4);
std::cout << std::boolalpha
<< (four_apples == four_oranges) << std::endl // false
<< (four_apples == five_apples) << std::endl // false
<< (four_oranges == four_more_oranges) << std::endl; // true
“It’s less important how these operations are defined and more
important that they are.” - Joe Armstrong
tagged_untyped::match<…>
int fruit_count(const aeternum::tagged_untyped& x) {
if (auto apples = x.match<fruit::apples, int>()) {
return *apples;
}
if (auto oranges = x.match<fruit::oranges, int>()) {
return *oranges;
}
return 0;
}
❖ Allows you to convert to tagged<T> provided that the
tag matches
❖ Allows you to define “polymorphic” functions without
complex inheritance hierarchies
tagged_untyped in containers
immer::set<aeternum::tagged_untyped> fruit;
fruit = fruit.insert(four_apples);
fruit = fruit.insert(four_oranges);
fruit = fruit.insert(five_apples);
fruit = fruit.insert(four_more_oranges);
for (auto& f : fruit) {
std::cout << "Set contains " << fruit_count(f)
<< " " << f.get_tag().name << std::endl;
}
❖ Containers requiring std::hash are supported
❖ Implementing std::less is future work, but possible
untyped_record
class untyped_record {
public:
using data = immer::map<atom, std::shared_ptr<void>>;
using hasher = std::function<std::size_t(...)>;
using equality_comparer = std::function<bool(...)>;
protected:
data _data;
hasher _hasher;
equality_comparer _equality_comparer;
};
❖ Based on a persistent map from atom to
std::shared_ptr<void>
❖ Also implements std::hash and operator==
fields<…>::record<…>
template<typename ...FieldTypes>
class fields {
public:
template<const atom& tag,
const field_name<FieldTypes>& ...names>
class record : public untyped_record {
public:
using tagged = tagged<record<tag, names...>>;
};
};
❖ Inherits from untyped_record and automatically supplies
structural equality comparison and hashing arguments
❖ Nested class because of variadic template restrictions (?)
field_name<T>
template<typename T>
class field_name : public lens<tagged<untyped_record>, T> {
private:
const atom _field_key;
};
❖ Inherits from lens<tagged<untyped_record>, T> and
contains an atom which is the key in the record’s data
map
❖ A lens is essentially a pair of getter and (persistent)
setter functions
Type-aliasing custom record<…>s
namespace contact {
constexpr aeternum::atom tag("contact");
const aeternum::field_name<std::string> telephone_("telephone");
const aeternum::field_name<std::string> email_("email");
using record =
aeternum::fields<std::string, std::string>
::record<tag, telephone_, email_>;
}
❖ Domain model definitions are done by type-aliasing
record in a namespace…
Manipulating custom record<…>s
auto const jane_contact =
contact::record::make("12345", “jane_bond@007.com");
std::cout << "Jane's telephone is "
<< jane_contact[contact::telephone_]
<< " and her email is "
<< jane_contact[contact::email_]
<< std::endl;
❖ field_name<T>s act as strongly-typed accessors via an
operator[] overload
Nesting custom record<…>s
namespace person {
constexpr aeternum::atom tag("person");
const aeternum::field_name<std::string> name_("name");
const aeternum::field_name<uint8_t> age_("age");
const aeternum::field_name<
contact::record::tagged> contact_("contact");
using record =
aeternum::fields<
std::string, uint8_t, contact::record::tagged>
::record<tag, name_, age_, contact_>;
}
❖ record<…>s can be composed into larger objects, and
equality comparison and hashing will take nested
records into account
Manipulating nested record<…>s
auto const john = person::record::make(
"John Smith", 42,
contact::record::make("67890", "j.smith@email.com"));
auto const junior = john
| person::name_.set("Johnny Junior”)
| person::age_.set(12);
auto const person_email_ = person::contact_ >> contact::email_;
auto const junior_new_email = junior
| person_email_.set("junior@email.com");
❖ field_name<T>::set creates a setter which acts on the
record and can be applied via operator|
❖ lens<A, B> and lens<B, C> can be stacked via operator>>
resulting in a lens<A, C>
Metadata fields and dynamism
❖ The underlying representation of a weakly-typed map
allows us to augment records with additional metadata
fields
❖ This can lead to less brittle system design: metadata
comes along for the ride and parts of the system that
don’t care about it just propagate it automatically
❖ This case is made well by Rich Hickey in “Maybe Not”:
https://www.youtube.com/watch?v=YR5WdGrpoug
❖ Dynamic data, polymorphic behaviour
Metadata: example
namespace music { namespace song {
constexpr aeternum::atom tag("song");
const aeternum::field_name<std::string> name_("name");
const aeternum::field_name<std::string> artist_("artist");
const aeternum::field_name<uint16_t> duration_("duration");
using record =
aeternum::fields<std::string, std::string, uint16_t>
::record<tag, name_, artist_, duration_>;
} }
❖ Imagine a music database: all songs have a name, artist,
and duration, but there is optional metadata such as
lyrics
Metadata: example
namespace music { namespace lyrics {
constexpr aeternum::atom tag("lyrics");
struct line {
std::string text;
uint16_t timestamp;
};
const aeternum::field_name<immer::vector<line>> lines_("lines");
const aeternum::field_name<std::string> author_("author");
using record =
aeternum::fields<immer::vector<line>, std::string>
::record<tag, lines_, author_>;
} }
namespace music { namespace metadata {
const aeternum::field_name<
music::lyrics::record::tagged> lyrics_("lyrics");
} }
Metadata: example
auto never_gonna = music::song::record::make(
"Never Gonna Give You Up",
"Rick Astley",
183);
never_gonna = never_gonna
| music::metadata::lyrics_.set(
music::lyrics::record::make(
immer::vector<music::lyrics::line> {
{ "Never gonna give you up", 22 },
{ "Never gonna let you down", 26 }
},
"rickroller89"));
Future work
❖ Implement serialisation and std::less out of the box
❖ Hashing and equality comparison should be augmented
when extending records with metadata
❖ Metadata fields should behave as prisms, not lenses
❖ Unit tests, perfect use case for RapidCheck:
https://github.com/emil-e/rapidcheck
❖ A better memory policy to avoid many small allocations
❖ Benchmarking (… also against Clojure)
Conclusions
❖ Persistent, dynamic maps can be made mostly
ergonomic in C++ with minor abuse of syntax
❖ Structural equality comparison and hashing allow you
to place these objects in collections
❖ Lenses allow you to manipulate deeply nested
immutable structures with ease

Weitere ähnliche Inhalte

Was ist angesagt?

Java Bytecode Fundamentals - JUG.lv
Java Bytecode Fundamentals - JUG.lvJava Bytecode Fundamentals - JUG.lv
Java Bytecode Fundamentals - JUG.lv
Anton Arhipov
 
Hacking Go Compiler Internals / GoCon 2014 Autumn
Hacking Go Compiler Internals / GoCon 2014 AutumnHacking Go Compiler Internals / GoCon 2014 Autumn
Hacking Go Compiler Internals / GoCon 2014 Autumn
Moriyoshi Koizumi
 

Was ist angesagt? (20)

Kotlin Bytecode Generation and Runtime Performance
Kotlin Bytecode Generation and Runtime PerformanceKotlin Bytecode Generation and Runtime Performance
Kotlin Bytecode Generation and Runtime Performance
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojure
 
Sailing with Java 8 Streams
Sailing with Java 8 StreamsSailing with Java 8 Streams
Sailing with Java 8 Streams
 
IO Streams, Files and Directories
IO Streams, Files and DirectoriesIO Streams, Files and Directories
IO Streams, Files and Directories
 
Java Bytecode Fundamentals - JUG.lv
Java Bytecode Fundamentals - JUG.lvJava Bytecode Fundamentals - JUG.lv
Java Bytecode Fundamentals - JUG.lv
 
Hacking Go Compiler Internals / GoCon 2014 Autumn
Hacking Go Compiler Internals / GoCon 2014 AutumnHacking Go Compiler Internals / GoCon 2014 Autumn
Hacking Go Compiler Internals / GoCon 2014 Autumn
 
Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
 
EuroPython 2016 - Do I Need To Switch To Golang
EuroPython 2016 - Do I Need To Switch To GolangEuroPython 2016 - Do I Need To Switch To Golang
EuroPython 2016 - Do I Need To Switch To Golang
 
Functional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting LambdasFunctional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting Lambdas
 
Seeking Clojure
Seeking ClojureSeeking Clojure
Seeking Clojure
 
Java Generics - by Example
Java Generics - by ExampleJava Generics - by Example
Java Generics - by Example
 
Design Patterns - Compiler Case Study - Hands-on Examples
Design Patterns - Compiler Case Study - Hands-on ExamplesDesign Patterns - Compiler Case Study - Hands-on Examples
Design Patterns - Compiler Case Study - Hands-on Examples
 
Why my Go program is slow?
Why my Go program is slow?Why my Go program is slow?
Why my Go program is slow?
 
Crystal presentation in NY
Crystal presentation in NYCrystal presentation in NY
Crystal presentation in NY
 
Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8
 
Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
 
Crystal internals (part 1)
Crystal internals (part 1)Crystal internals (part 1)
Crystal internals (part 1)
 
Few simple-type-tricks in scala
Few simple-type-tricks in scalaFew simple-type-tricks in scala
Few simple-type-tricks in scala
 
Os Goodger
Os GoodgerOs Goodger
Os Goodger
 
Python idiomatico
Python idiomaticoPython idiomatico
Python idiomatico
 

Ähnlich wie Pseudo dynamic immutable records in C++

An Annotation Framework for Statically-Typed Syntax Trees
An Annotation Framework for Statically-Typed Syntax TreesAn Annotation Framework for Statically-Typed Syntax Trees
An Annotation Framework for Statically-Typed Syntax Trees
Ray Toal
 
computer notes - Data Structures - 8
computer notes - Data Structures - 8computer notes - Data Structures - 8
computer notes - Data Structures - 8
ecomputernotes
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
HamletDRC
 
Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)
ujihisa
 
Hacking Parse.y with ujihisa
Hacking Parse.y with ujihisaHacking Parse.y with ujihisa
Hacking Parse.y with ujihisa
ujihisa
 

Ähnlich wie Pseudo dynamic immutable records in C++ (20)

Summary of C++17 features
Summary of C++17 featuresSummary of C++17 features
Summary of C++17 features
 
Antlr V3
Antlr V3Antlr V3
Antlr V3
 
An Annotation Framework for Statically-Typed Syntax Trees
An Annotation Framework for Statically-Typed Syntax TreesAn Annotation Framework for Statically-Typed Syntax Trees
An Annotation Framework for Statically-Typed Syntax Trees
 
Achieving Parsing Sanity In Erlang
Achieving Parsing Sanity In ErlangAchieving Parsing Sanity In Erlang
Achieving Parsing Sanity In Erlang
 
Static Analysis in Go
Static Analysis in GoStatic Analysis in Go
Static Analysis in Go
 
Apidays Paris 2023 - Forget TypeScript, Choose Rust to build Robust, Fast and...
Apidays Paris 2023 - Forget TypeScript, Choose Rust to build Robust, Fast and...Apidays Paris 2023 - Forget TypeScript, Choose Rust to build Robust, Fast and...
Apidays Paris 2023 - Forget TypeScript, Choose Rust to build Robust, Fast and...
 
Introduction to source{d} Engine and source{d} Lookout
Introduction to source{d} Engine and source{d} Lookout Introduction to source{d} Engine and source{d} Lookout
Introduction to source{d} Engine and source{d} Lookout
 
computer notes - Data Structures - 8
computer notes - Data Structures - 8computer notes - Data Structures - 8
computer notes - Data Structures - 8
 
Round PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing FunctionallyRound PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing Functionally
 
Bioinformatica: Esercizi su Perl, espressioni regolari e altre amenità (BMR G...
Bioinformatica: Esercizi su Perl, espressioni regolari e altre amenità (BMR G...Bioinformatica: Esercizi su Perl, espressioni regolari e altre amenità (BMR G...
Bioinformatica: Esercizi su Perl, espressioni regolari e altre amenità (BMR G...
 
Java Annotations and Pre-processing
Java  Annotations and Pre-processingJava  Annotations and Pre-processing
Java Annotations and Pre-processing
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
C++ theory
C++ theoryC++ theory
C++ theory
 
Clean & Typechecked JS
Clean & Typechecked JSClean & Typechecked JS
Clean & Typechecked JS
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard Library
 
Odp
OdpOdp
Odp
 
Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)
 
Hacking Parse.y with ujihisa
Hacking Parse.y with ujihisaHacking Parse.y with ujihisa
Hacking Parse.y with ujihisa
 
Rust LDN 24 7 19 Oxidising the Command Line
Rust LDN 24 7 19 Oxidising the Command LineRust LDN 24 7 19 Oxidising the Command Line
Rust LDN 24 7 19 Oxidising the Command Line
 
7 Common mistakes in Go and when to avoid them
7 Common mistakes in Go and when to avoid them7 Common mistakes in Go and when to avoid them
7 Common mistakes in Go and when to avoid them
 

Kürzlich hochgeladen

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 

Kürzlich hochgeladen (20)

8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 

Pseudo dynamic immutable records in C++

  • 1. If one cannot get one’s own way, one must bow to the inevitable... Pseudo-dynamic immutable records in C++ Anton Tcholakov Software Engineer, Pico Technology
  • 2. About ❖ Software engineer working on drivers at Pico Technology ❖ Physics graduate with experience in experimental physics ❖ Amateur photographer
  • 3. About ❖ Software engineer working on drivers at Pico Technology ❖ Physics graduate with experience in experimental physics ❖ Amateur photographer
  • 4. About ❖ Software engineer working on drivers at Pico Technology ❖ Physics graduate with experience in experimental physics ❖ Amateur photographer
  • 5. Pseudo-dynamic immutable records “If it walks like a duck and quacks like a duck then it must be a duck.” class Duck: def fly(self): print("Duck flying") class Airplane: def fly(self): print("Airplane flying") for thing in Duck(), Airplane(): thing.fly()
  • 6. Pseudo-dynamic immutable records immutable (adj.) unchanging over time or unable to be changed -module(lists). -export(sum/1). sum([], acc) -> acc; sum([First|Rest], acc) -> sum(Rest, First + acc). sum(List) -> sum(List, 0).
  • 7. Pseudo-dynamic immutable records composite data type (n.) … composed from primitives and other composites type Account = { AccountNumber : int FirstName : string LastName : string Balance : float }
  • 8. … in C++. Why?! ❖ Having different mental models for how to deconstruct problems is incredibly valuable ❖ Functional techniques are very well-suited to programming concurrent systems ❖ C++ is a very malleable programming language ❖ Intellectual curiosity…
  • 9. Building on immer https://github.com/arximboldi/immer immutable, persistent data structures for C++ #include <immer/vector.hpp> int main() { const auto v0 = immer::vector<int>{}; const auto v1 = v0.push_back(13); assert(v0.size() == 0 && v1.size() == 1 && v1[0] == 13); const auto v2 = v1.set(0, 42); assert(v1[0] == 13 && v2[0] == 42); }
  • 11. aeternum ❖ Name is Latin for “forever”… ❖ Currently just a playground ❖ https://github.com/anton-pt/aeternum
  • 12. atoms ❖ Literal types represented by C-style strings ❖ Name is hashed using CRC-32 at compile time ❖ Useful as “tags” for dynamic types and key-value pairs constexpr aeternum::atom apples("apples"); constexpr aeternum::atom oranges("oranges"); std::cout << std::boolalpha << (apples == oranges); // output: false
  • 13. tagged_untyped and tagged<T> ❖ tagged<T> inherits from tagged_untyped ❖ Supports std::hash and operator== provided that T does const auto four_apples = aeternum::make_tagged(apples, 4); const auto four_oranges = aeternum::make_tagged(oranges, 4); const auto five_apples = aeternum::make_tagged(apples, 5); const auto four_more_oranges = aeternum::make_tagged(oranges, 4); std::cout << std::boolalpha << (four_apples == four_oranges) << std::endl // false << (four_apples == five_apples) << std::endl // false << (four_oranges == four_more_oranges) << std::endl; // true “It’s less important how these operations are defined and more important that they are.” - Joe Armstrong
  • 14. tagged_untyped::match<…> int fruit_count(const aeternum::tagged_untyped& x) { if (auto apples = x.match<fruit::apples, int>()) { return *apples; } if (auto oranges = x.match<fruit::oranges, int>()) { return *oranges; } return 0; } ❖ Allows you to convert to tagged<T> provided that the tag matches ❖ Allows you to define “polymorphic” functions without complex inheritance hierarchies
  • 15. tagged_untyped in containers immer::set<aeternum::tagged_untyped> fruit; fruit = fruit.insert(four_apples); fruit = fruit.insert(four_oranges); fruit = fruit.insert(five_apples); fruit = fruit.insert(four_more_oranges); for (auto& f : fruit) { std::cout << "Set contains " << fruit_count(f) << " " << f.get_tag().name << std::endl; } ❖ Containers requiring std::hash are supported ❖ Implementing std::less is future work, but possible
  • 16. untyped_record class untyped_record { public: using data = immer::map<atom, std::shared_ptr<void>>; using hasher = std::function<std::size_t(...)>; using equality_comparer = std::function<bool(...)>; protected: data _data; hasher _hasher; equality_comparer _equality_comparer; }; ❖ Based on a persistent map from atom to std::shared_ptr<void> ❖ Also implements std::hash and operator==
  • 17. fields<…>::record<…> template<typename ...FieldTypes> class fields { public: template<const atom& tag, const field_name<FieldTypes>& ...names> class record : public untyped_record { public: using tagged = tagged<record<tag, names...>>; }; }; ❖ Inherits from untyped_record and automatically supplies structural equality comparison and hashing arguments ❖ Nested class because of variadic template restrictions (?)
  • 18. field_name<T> template<typename T> class field_name : public lens<tagged<untyped_record>, T> { private: const atom _field_key; }; ❖ Inherits from lens<tagged<untyped_record>, T> and contains an atom which is the key in the record’s data map ❖ A lens is essentially a pair of getter and (persistent) setter functions
  • 19. Type-aliasing custom record<…>s namespace contact { constexpr aeternum::atom tag("contact"); const aeternum::field_name<std::string> telephone_("telephone"); const aeternum::field_name<std::string> email_("email"); using record = aeternum::fields<std::string, std::string> ::record<tag, telephone_, email_>; } ❖ Domain model definitions are done by type-aliasing record in a namespace…
  • 20. Manipulating custom record<…>s auto const jane_contact = contact::record::make("12345", “jane_bond@007.com"); std::cout << "Jane's telephone is " << jane_contact[contact::telephone_] << " and her email is " << jane_contact[contact::email_] << std::endl; ❖ field_name<T>s act as strongly-typed accessors via an operator[] overload
  • 21. Nesting custom record<…>s namespace person { constexpr aeternum::atom tag("person"); const aeternum::field_name<std::string> name_("name"); const aeternum::field_name<uint8_t> age_("age"); const aeternum::field_name< contact::record::tagged> contact_("contact"); using record = aeternum::fields< std::string, uint8_t, contact::record::tagged> ::record<tag, name_, age_, contact_>; } ❖ record<…>s can be composed into larger objects, and equality comparison and hashing will take nested records into account
  • 22. Manipulating nested record<…>s auto const john = person::record::make( "John Smith", 42, contact::record::make("67890", "j.smith@email.com")); auto const junior = john | person::name_.set("Johnny Junior”) | person::age_.set(12); auto const person_email_ = person::contact_ >> contact::email_; auto const junior_new_email = junior | person_email_.set("junior@email.com"); ❖ field_name<T>::set creates a setter which acts on the record and can be applied via operator| ❖ lens<A, B> and lens<B, C> can be stacked via operator>> resulting in a lens<A, C>
  • 23. Metadata fields and dynamism ❖ The underlying representation of a weakly-typed map allows us to augment records with additional metadata fields ❖ This can lead to less brittle system design: metadata comes along for the ride and parts of the system that don’t care about it just propagate it automatically ❖ This case is made well by Rich Hickey in “Maybe Not”: https://www.youtube.com/watch?v=YR5WdGrpoug ❖ Dynamic data, polymorphic behaviour
  • 24. Metadata: example namespace music { namespace song { constexpr aeternum::atom tag("song"); const aeternum::field_name<std::string> name_("name"); const aeternum::field_name<std::string> artist_("artist"); const aeternum::field_name<uint16_t> duration_("duration"); using record = aeternum::fields<std::string, std::string, uint16_t> ::record<tag, name_, artist_, duration_>; } } ❖ Imagine a music database: all songs have a name, artist, and duration, but there is optional metadata such as lyrics
  • 25. Metadata: example namespace music { namespace lyrics { constexpr aeternum::atom tag("lyrics"); struct line { std::string text; uint16_t timestamp; }; const aeternum::field_name<immer::vector<line>> lines_("lines"); const aeternum::field_name<std::string> author_("author"); using record = aeternum::fields<immer::vector<line>, std::string> ::record<tag, lines_, author_>; } } namespace music { namespace metadata { const aeternum::field_name< music::lyrics::record::tagged> lyrics_("lyrics"); } }
  • 26. Metadata: example auto never_gonna = music::song::record::make( "Never Gonna Give You Up", "Rick Astley", 183); never_gonna = never_gonna | music::metadata::lyrics_.set( music::lyrics::record::make( immer::vector<music::lyrics::line> { { "Never gonna give you up", 22 }, { "Never gonna let you down", 26 } }, "rickroller89"));
  • 27. Future work ❖ Implement serialisation and std::less out of the box ❖ Hashing and equality comparison should be augmented when extending records with metadata ❖ Metadata fields should behave as prisms, not lenses ❖ Unit tests, perfect use case for RapidCheck: https://github.com/emil-e/rapidcheck ❖ A better memory policy to avoid many small allocations ❖ Benchmarking (… also against Clojure)
  • 28. Conclusions ❖ Persistent, dynamic maps can be made mostly ergonomic in C++ with minor abuse of syntax ❖ Structural equality comparison and hashing allow you to place these objects in collections ❖ Lenses allow you to manipulate deeply nested immutable structures with ease