Declarative Style in CPP Ben Deane Cppcon 2018
Declarative Style in CPP Ben Deane Cppcon 2018
1
IN THIS TALK
1. Definitions & motivation
2. Where we came from
3. Where we are
4. Where we could be headed
2.1
WHAT DO WE MEAN?
– wiki.c2.com
3.1
DECLARATIVE STYLE INDICATORS
3.2
DECLARATIVE STYLE INDICATORS
referential transparency
3.2
DECLARATIVE STYLE INDICATORS
referential transparency
say WHAT in preference to HOW
3.2
DECLARATIVE STYLE INDICATORS
referential transparency
say WHAT in preference to HOW
minimize imperative style
3.2
DECLARATIVE STYLE INDICATORS
referential transparency
say WHAT in preference to HOW
minimize imperative style
declaring things
3.2
DECLARATIVE STYLE INDICATORS
referential transparency
say WHAT in preference to HOW
minimize imperative style
declaring things
expressions over statements
3.2
EXPRESSIONS VS STATEMENTS
4.1
EXPRESSIONS
Properties of expressions:
4.2
EXPRESSIONS
Properties of expressions:
value category
4.2
EXPRESSIONS
Properties of expressions:
value category
type
4.2
EXPRESSIONS COMPOSE ON MULTIPLE AXES
auto expr = a @ b @ c;
4.3
STATEMENTS
Properties of statements:
4.4
STATEMENTS
Properties of statements:
er…
4.4
STATEMENTS "COMPOSE" ONLY BY SEQUENCING
x;
y;
z;
no type checking
value checking is manual, intrusive
implicit constraints
temporal reasoning is poor
4.5
IMPERATIVE SAFETY GEAR
Many of our guidelines, best practices, idioms, and much of our tooling, analysis, and
brainpower work in service of checking the implicit constraints around statement
"composition".
4.6
DECLARATIVE STYLE: AVOID STATEMENTS!
expression statement
selection statement (if, switch)
iteration statement (for, while, do)
jump statement (break, continue, return, goto)
declaration statement
4.7
LET'S EXAMINE HISTORY…
Let's look at where we've come from, and see how it informs moving to declarative
style.
5.1
WORLD'S LAST BUG
while (true)
{
status = GetRadarInfo();
if (status = 1)
LaunchMissiles();
}
5.2
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
5.3
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
if (a = n / b)
printn(a, b); /* recursive */
putchar(n % b + '0');
}
5.4
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
We've learned to deal with this. But we don't really like it.
5.5
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
We've learned to deal with this. But we don't really like it.
yoda conditions
5.5
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
We've learned to deal with this. But we don't really like it.
yoda conditions
compiler warnings
5.5
ODD THING #1: ASSIGNMENTS ARE EXPRESSIONS
We've learned to deal with this. But we don't really like it.
yoda conditions
compiler warnings
P0963: discouraged
5.5
ODD THING #2: = MEANS ASSIGNMENT
5.6
ODD THING #2: = MEANS ASSIGNMENT
"A notorious example for a bad idea was the choice of the equal
sign to denote assignment."
– Niklaus Wirth
5.7
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
5.8
ODD THING #2: = MEANS ASSIGNMENT
– Ken Thompson
5.9
DECLARATION VS (RE-)ASSIGNMENT
a = 1729; // assignment
5 . 10
<END OF HISTORICAL DIVERSION>
5 . 11
<END OF HISTORICAL DIVERSION>
5 . 11
<END OF HISTORICAL DIVERSION>
5 . 11
<END OF HISTORICAL DIVERSION>
5 . 11
DECLARATIVE STYLE: AVOIDING STATEMENTS
Statement Status
assignment
selection
iteration
jump
declaration
5 . 12
A QUICK DECLARATIVE STUDY
6.1
EXAMPLE
Given:
weak_ptr<Foo> wp;
How to write:
Bar b;
{
auto sp = wp.lock();
if (sp) b = sp->bar();
}
6.2
C++17 IF-INITIALIZER?
Bar b;
if (auto sp = wp.lock(); sp)
b = sp->bar();
This still has the declaration/initialization split. Still has mutable state.
6.3
CONDITIONAL OPERATOR?
Hm…
6.4
C++?? CONDITIONAL-OPERATOR-INITIALIZER?
6.5
GCC EXTENSION?
Bar b =
({
auto sp = wp.lock();
sp ? sp->bar() : Bar{};
});
6.6
I+LE?
Bar b = [&] () {
if (auto sp = wp.lock(); sp) return sp->bar();
return Bar{};
}();
6.7
OPTIONAL-LIKE?
6.8
FUNCTORIAL/MONADIC INTERFACE?
shared_ptr<Bar> b = fmap(wp.lock(),
[] (auto foo) { return foo.bar(); });
6.9
STUDY CONCLUSIONS
6 . 10
EXISTING DECLARATIVE PRACTICE
7.1
CORE GUIDELINES
7.2
DECLARATIVE STYLE: AVOIDING STATEMENTS
7.3
FUNCTIONS IN GENERAL
7.4
ANOTHER REASON TO LIKE FUNCTIONS
7.5
<ALGORITHM>
7.6
#include "my_algorithms.h"
min_unused
is_prefix_of
join
transform_if
set_differences (aka before and after)
push_back_unique
7.7
DECLARATIVE STYLE: AVOIDING STATEMENTS
7.8
DECLARATIVE DOMAINS AND
PATTERNS
8.1
TESTING
8.2
TESTING
8.2
TESTING
8.2
TESTING
8.2
TESTING
8.2
LOGGING : IMPERATIVE TURNED DECLARATIVE
vs
LOG("R Tape loading error, " << line << ':' << stmt);
8.3
WHERE DID THE GLOBAL GO?
8.4
C-STYLE LOG SINK
8.5
LOG SINKS: OO TURNED DECLARATIVE
8.6
SINK VARIATIONS
8.7
SINK VARIATIONS
8.8
SINK VARIATIONS
8.9
SINK VARIATIONS
8 . 10
SINK VARIATIONS
8 . 11
DECLARATIVE SINK CONSTRUCTION
8 . 12
DECLARATIVE STYLE: AVOIDING STATEMENTS
8 . 13
DESIGN PATTERNS
9.1
OO PATTERNS
9.2
THE "BUILDER PATTERN"
"In which the author turns what should be 5 lines of glut calls at
the start of main into 100 lines of buggy OOP."
9.3
BUILDER PATTERN: A BETTER EXAMPLE
// Schedule& Schedule::then(interval_t);
auto s = Schedule(interval::fixed{1s})
.then(repeat::n_times{5, interval::random_exponential{2s, 2.0}})
.then(repeat::forever{interval::fixed{30s}});
9.4
BUILDER PATTERN: HELP FROM C++17
9.5
PUTTING TYPES TO WORK
// Use it
send_request(req);
9.6
PUTTING TYPES TO WORK
field_t f1;
// etc ...
};
9.7
BEHAVIOUR IN THE TYPE
9.8
BEHAVIOUR IN THE TYPE
template <uint8_t N>
struct request_t;
template <>
struct request_t<0>
{
field_t f1;
// etc ...
};
9.9
BEHAVIOUR IN THE TYPE
Use =delete to enable the send_request function only for a correctly-filled-in request.
request_t<ALL_FIELDS> make_request();
9 . 10
BUILDER PATTERN GUIDELINES
9 . 11
WHERE CAN WE GO FROM HERE?
10 . 1
RAII, INITIALIZATION
10 . 2
FUNCTIONS & LAMBDAS
Functions:
turn statements into expressions
give expressions names
encapsulate conditions
are the optimizer's bread and butter (RVO, inlining)
Structured bindings work around single-return-value limitation.
10 . 3
OVERLOADS & TEMPLATES
10 . 4
OVERLOADS & TEMPLATES
class uniform_duration_distribution;
10 . 5
INCONSISTENCIES
10 . 6
HERITAGE: OPERATORS
C++ inherits pretty much all of its operators from C (or even earlier).
We also inherit some fixed semantics (despite operator overloading).
Operators can be amazing for expressivity of code and declarative constructs.
10 . 7
"IMPERATIVE SAFETY GEAR"
10 . 8
"IMPERATIVE SAFETY GEAR"
better warnings
10 . 8
"IMPERATIVE SAFETY GEAR"
better warnings
static analysis
10 . 8
"IMPERATIVE SAFETY GEAR"
better warnings
static analysis
[[nodiscard]] attribute (another default?)
10 . 8
"IMPERATIVE SAFETY GEAR"
better warnings
static analysis
[[nodiscard]] attribute (another default?)
[[fallthrough]] attribute
10 . 8
"IMPERATIVE SAFETY GEAR"
better warnings
static analysis
[[nodiscard]] attribute (another default?)
[[fallthrough]] attribute
if-initializer
10 . 8
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 9
RICHNESS OF LIBRARY HELP
10 . 10
GUIDELINES FOR DECLARATIVE CODE
11 . 1
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 2
REPLACING CONDITIONALS
11 . 3
REPLACING CONDITIONALS
11 . 3
REPLACING CONDITIONALS
11 . 3
REPLACING CONDITIONALS
11 . 3
REPLACING CONDITIONALS => FEWER STATEMENTS
11 . 4
REPLACING LOOPS => FEWER STATEMENTS
11 . 5
REPLACING ASSIGNMENTS
Declare-at-use
use I+LEs
leverage const
use AAA-style if you like
Overload operators for declaration power
11 . 6
LET THE LANGUAGE HELP
11 . 7
DECLARATIVE INTERFACES
dependency injection
higher-order functions
builder pattern / fluent style
identify monoids
start with composition
11 . 8
DECLARATIVE GOALS
11 . 9