Functional Programming in C++
and concurrent calculations
graninas@gmail.comAlexander Granin
Plan
● Functional Programming in C++
● Concurrent models
● Software Transactional Memory
● Monadic STM in C++
● Advanced Functional Programming in C++
Functional Programming in C++
Bartosz Milewski
Eric Niebler
Ivan Čukić
John Carmack
template<template<typename> class Monad>
auto run()
{
return
DO(Monad,
(x, unit(1))
(y, unit(2))
(z, DO(Monad,
(x, unit(5))
(_, unit(x - 2))
))
(_, unit(x + y + z))
);
}
Introduction to FP in C++: Monads!
Evgeny Panasyuk
https://github.com/evgeny-panasyuk/monad_do
Concurrent models
shared
resource
actor actor
std::shared_ptr std::shared_ptrint refCounter;
struct Chest
{
int iron;
int copper;
int coal;
};
iron (1)
copper (1)
copper (1)
iron (1)
coal (1)
struct Chest
{
std::atomic<int> iron;
std::atomic<int> copper;
std::atomic<int> coal;
};
iron (1)
copper (1)
copper (1)
iron (1)
coal (1)
struct Chest
{
std::atomic<int> iron;
std::atomic<int> copper;
std::atomic<int> coal;
};
iron < 100
copper < 100
coal > 6 && iron > 3
coal > 6 && copper > 3
iron (1)
copper (1)
coal (3), copper (3)
coal (3), iron (3)
coal (1)
coal < 100
struct Chest
{
int iron;
int copper;
int coal;
std::mutex lock;
};
iron < 100
copper < 100
coal > 6 && iron > 3
coal > 6 && copper > 3
iron (1)
copper (1)
coal (3), copper (3)
coal (3), iron (3)
coal (1)
coal < 100
Software Transactional Memory
Wyatt-STM
https://github.com/bretthall/Wyatt-STM
CppCon 2015: Brett Hall “Transactional Memory in Practice"
cpp_stm_free
https://github.com/graninas/cpp_stm_free
C++ Russia 2018 (Spb): “Functional approach to STM”
Transactions
struct Chest
{
stm::TVar<int> iron;
stm::TVar<int> copper;
stm::TVar<int> coal;
};
struct Chest
{
stm::TVar<int> iron;
stm::TVar<int> copper;
stm::TVar<int> coal;
};
STM-based concurrency
stm::STML<stm::Unit> insert(stm::TVar<int> resource, int count);
stm::STML<stm::Unit> extract( stm::TVar<int> resource1, int count1,
stm::TVar<int> resource2, int count2 );
struct Chest
{
stm::TVar<int> iron;
stm::TVar<int> copper;
stm::TVar<int> coal;
};
STM-based concurrency
stm::STML<stm::Unit> insert(stm::TVar<int> resource, int count);
stm::STML<stm::Unit> extract( stm::TVar<int> resource1, int count1,
stm::TVar<int> resource2, int count2 );
stm::Context ctx;
stm::atomically(ctx, insert(chest.iron, 1) ); // Thread 1
stm::atomically(ctx, insert(chest.copper, 1) ); // Thread 2
stm::atomically(ctx, insert(chest.coal, 1) ); // Thread 3
stm::atomically(ctx, extract(chest.coal, 3, chest.iron, 3) ); // Thread 4
stm::atomically(ctx, extract(chest.coal, 3, chest.copper, 3) ); // Thread 5
struct Chest
{
stm::TVar<int> iron;
stm::TVar<int> copper;
stm::TVar<int> coal;
};
STM-based concurrency
Monadic STM in C++
template <typename A>
STML<TVar<A>> newTVar(const A& val);
STM: TVar operations
newTVar :: a → STML (TVar a)
template <typename A>
STML<TVar<A>> newTVar(const A& val);
template <typename A>
STML<A> readTVar(const TVar<A>& tvar);
STM: TVar operations
newTVar :: a → STML (TVar a)
readTVar :: TVar a → STML a
template <typename A>
STML<TVar<A>> newTVar(const A& val);
template <typename A>
STML<A> readTVar(const TVar<A>& tvar);
template <typename A>
STML<Unit> writeTVar(const TVar<A>& tvar,
const A& val);
STM: TVar operations
newTVar :: a → STML (TVar a)
readTVar :: TVar a → STML a
writeTVar :: TVar a → a → STML ()
STM: Monadically composable transactions
transaction :: STML Int
transaction = do
tvar ← newTVar 10
readTVar tvar
STML<int> transaction() {
STML<TVar<int>> tvarTrans = newTVar(10);
// return readTVar(tvarTrans);
// ????
}
STM: Monadically composable transactions
transaction :: STML Int
transaction = do
tvar ← newTVar 10
readTVar tvar
transaction :: STML Int
transaction =
bind (newTVar 10) (tvar → readTVar tvar)
STML<int> transaction() {
STML<TVar<int>> tvarTrans = newTVar(10);
// return readTVar(tvarTrans);
// ????
}
STML<int> transaction() {
STML<TVar<int>> tvarTrans = newTVar(10);
return bind(tvarTrans,
[](TVar<int> tvar) {
return readTVar(tvar);
});
}
Sample: Extract resources transaction
STML<Unit> extract (TVar<int> resource1, int count1, TVar<int> resource2, int count2) {
STML<Unit> t1 = extractResource (resource1, count1);
STML<Unit> t2 = extractResource (resource2, count2);
return bind (t1, [=](Unit) { return t2; });
}
STML<Unit> extractResource (TVar<int> resource, int count) {
STML<int> trans1 = readTVar (resource);
return bind (trans1, [=](int value) {
return value >= count
? writeTVar (recource, value - count)
: retry();
});
}
STM: C++ Interface
template <typename A, typename B>
STML<B> bind(const STML<A> ma,
const std::function<STML<B>(A)>& f);
template <typename A>
STML<A> pure(const A& val);
template <typename A>
STML<A> retry();
template <typename A>
STML<TVar<A>> newTVar(const A& val);
template <typename A>
STML<A> readTVar(const TVar<A>& tvar);
template <typename A>
STML<Unit> writeTVar(const TVar<A>& tvar,
const A& val);
Advanced Functional Programming in C++
Design Pattern: eDSL
Language ScenarioComposition
Environment 1
Environment 2
...
Interpreting
(Evaluation)
template <typename A, typename Ret>
struct NewTVar
{
A val;
std::function<Ret(TVar<A>)> next;
};
template <typename A, typename Ret>
struct ReadTVar
{
TVar<A> tvar;
std::function<Ret(A)> next;
};
template <typename A, typename Ret>
struct WriteTVar
{
TVar<A> tvar;
A val;
std::function<Ret(fp::Unit)> next;
};
template <typename A, typename Ret>
struct Retry
{
};
STM eDSL
template <class Ret>
struct STMF
{
std::variant<NewTVar <std::any, Ret>,
ReadTVar <std::any, Ret>,
WriteTVar <std::any, Ret>,
Retry <std::any, Ret>
> stmf;
};
STM eDSL Generalized ADT
data STMF next where
NewTVar :: a -> (TVar a -> next) -> STMF next
WriteTVar :: TVar a -> a -> next -> STMF next
ReadTVar :: TVar a -> (a -> next) -> STMF next
Retry :: STMF next
template <typename A, typename B>
struct StmfFunctorVisitor
{
STMF<B> result;
void operator() (const NewTVar<std::any, STML<A>>& method);
void operator() (const ReadTVar<std::any, STML<A>>& method);
void operator() (const WriteTVar<std::any, STML<A>>& method);
void operator() (const Retry <std::any, STML<A>>&);
};
template <typename A, typename B>
STMF<B> fmap(const std::function<B(A)>& f, const STMF<A>& method) {
StmfFunctorVisitor<A, B> visitor(f);
std::visit(visitor, method.stmf);
return visitor.result;
}
STM eDSL ADT interpreting (“Pattern Matching”)
Monadic STML chains
transaction :: STML Int
transaction = do
tvar ← newTVar 42
v1 ← readTVar tvar
v2 ← pure 10
pure (v1 + v2)
STML<int> transaction()
{
return bind (newTVar (42), [=](TVar<int> tvar)
{
return bind (readTVar (tvar), [=](int v1)
{
return bind (pure (10), [=](int v2)
{
return pure (v1 + v2);
});
});
});
}
STML<int>
↳ STML<TVar<int>>
↳ STML<int>
↳ STML<int>
↳ STML<int>
Recursive STML type
STML<int> transaction()
{
return bind (newTVar (42), [=](TVar<int> tvar)
{
return bind (readTVar (tvar), [=](int v1)
{
return bind (pure (10), [=](int v2)
{
return pure (v1 + v2);
});
});
});
}
STML<int>
[](){}
↳ STML<TVar<int>>
[](){}
↳ STML<int>
[](){}
↳ STML<int>
[](){}
↳ STML<int>
Recursive STML type (lambda-enclosed)
STML<int> transaction()
{
return bind (newTVar (42), [=](TVar<int> tvar)
{
return bind (readTVar (tvar), [=](int v1)
{
return bind (pure (10), [=](int v2)
{
return pure (v1 + v2);
});
});
});
}
Free Monads
Free monad STM
NewTVar
WriteTVar
ReadTVar Retry
bind, pure STML<A>
“transaction”
Free ScenarioFree monad composition
STM eDSL (ADT)
Free monad (ADT)
Interpreting
Conflicts
Resolving Commit or Retry
Free monads
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
Binding complexity: O(n2
)
<stm/free/stm.h>
Free monads
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
-- Free monad, Church-encoded form
data ChurchFree f a
= ChurchFree { runChurch :: forall z. (a -> z)
-> (f z -> z)
-> z }
Binding complexity: O(n2
)
<stm/free/stm.h>
Binding complexity: O(n)
<stm/church/stm.h>
(10x faster)
Free monads
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
-- Free monad, Church-encoded form
data ChurchFree f a
= ChurchFree { runChurch :: forall z. (a -> z)
-> (f z -> z)
-> z }
-- Free monad, Scott-encoded form
data Free a = Free { runFree :: forall u . (a -> u)
-> (f (Free f a) -> u)
-> u }
Binding complexity: O(n2
)
<stm/free/stm.h>
Binding complexity: O(n)
<stm/church/stm.h>
(10x faster)
Binding complexity: O(n2
)
STML Free monad type (normal form)
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
-- STML Free monad type
type STML a = Free STMF a
STML Free monad type (normal form)
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
-- STML Free monad type
type STML a = Free STMF a
f ~ STMF
f (Free f a) ~ STMF (Free STMF a)
f (Free f a) ~ STMF (STML a)
STML Free monad type (normal form)
-- Free monad, normal form
data Free f a = Pure a
| Bind (f (Free f a))
-- STML Free monad type
type STML a = Free STMF a
f ~ STMF
f (Free f a) ~ STMF (Free STMF a)
f (Free f a) ~ STMF (STML a)
template <typename Ret>
struct STML {
std::variant<PureF<Ret>, BindF<Ret>> stml;
};
template <typename Ret>
struct PureF {
Ret ret;
};
template <typename Ret>
struct BindF {
STMF<STML<Ret>> stmf;
};
STML Free monad type (normal form)
template <typename Ret>
struct STML {
std::variant<PureF<Ret>, BindF<Ret>> stml;
};
template <typename Ret>
struct PureF {
Ret ret;
};
template <typename Ret>
struct BindF {
STMF<STML<Ret>> stmf;
};
template <class Ret>
struct STMF {
std::variant<NewTVar <std::any, Ret>,
ReadTVar <std::any, Ret>,
WriteTVar <std::any, Ret>,
Retry <std::any, Ret>
> stmf;
};
template <typename A, typename Ret>
struct NewTVar {
A val;
std::function<Ret(TVar<A>)> next;
};
STML monadic binding
template <typename A, typename B>
struct BindStmlVisitor;
template <typename A, typename B>
struct BindStmfVisitor;
template <typename A, typename B>
STML<B> bind(const STML<A>& stml, const std::function<STML<B>(A)>& f)
{
BindStmlVisitor<A, B> visitor(f);
std::visit(visitor, stml.stml);
return visitor.result;
}
Thank you for
watching!
Alexander Granin
graninas@gmail.com

Functional programming in C++ LambdaNsk

  • 1.
    Functional Programming inC++ and concurrent calculations graninas@gmail.comAlexander Granin
  • 2.
    Plan ● Functional Programmingin C++ ● Concurrent models ● Software Transactional Memory ● Monadic STM in C++ ● Advanced Functional Programming in C++
  • 3.
    Functional Programming inC++ Bartosz Milewski Eric Niebler Ivan Čukić John Carmack
  • 4.
    template<template<typename> class Monad> autorun() { return DO(Monad, (x, unit(1)) (y, unit(2)) (z, DO(Monad, (x, unit(5)) (_, unit(x - 2)) )) (_, unit(x + y + z)) ); } Introduction to FP in C++: Monads! Evgeny Panasyuk https://github.com/evgeny-panasyuk/monad_do
  • 5.
  • 6.
  • 10.
    struct Chest { int iron; intcopper; int coal; }; iron (1) copper (1) copper (1) iron (1) coal (1)
  • 11.
    struct Chest { std::atomic<int> iron; std::atomic<int>copper; std::atomic<int> coal; }; iron (1) copper (1) copper (1) iron (1) coal (1)
  • 12.
    struct Chest { std::atomic<int> iron; std::atomic<int>copper; std::atomic<int> coal; }; iron < 100 copper < 100 coal > 6 && iron > 3 coal > 6 && copper > 3 iron (1) copper (1) coal (3), copper (3) coal (3), iron (3) coal (1) coal < 100
  • 13.
    struct Chest { int iron; intcopper; int coal; std::mutex lock; }; iron < 100 copper < 100 coal > 6 && iron > 3 coal > 6 && copper > 3 iron (1) copper (1) coal (3), copper (3) coal (3), iron (3) coal (1) coal < 100
  • 15.
    Software Transactional Memory Wyatt-STM https://github.com/bretthall/Wyatt-STM CppCon2015: Brett Hall “Transactional Memory in Practice" cpp_stm_free https://github.com/graninas/cpp_stm_free C++ Russia 2018 (Spb): “Functional approach to STM”
  • 16.
  • 17.
    struct Chest { stm::TVar<int> iron; stm::TVar<int>copper; stm::TVar<int> coal; }; STM-based concurrency
  • 18.
    stm::STML<stm::Unit> insert(stm::TVar<int> resource,int count); stm::STML<stm::Unit> extract( stm::TVar<int> resource1, int count1, stm::TVar<int> resource2, int count2 ); struct Chest { stm::TVar<int> iron; stm::TVar<int> copper; stm::TVar<int> coal; }; STM-based concurrency
  • 19.
    stm::STML<stm::Unit> insert(stm::TVar<int> resource,int count); stm::STML<stm::Unit> extract( stm::TVar<int> resource1, int count1, stm::TVar<int> resource2, int count2 ); stm::Context ctx; stm::atomically(ctx, insert(chest.iron, 1) ); // Thread 1 stm::atomically(ctx, insert(chest.copper, 1) ); // Thread 2 stm::atomically(ctx, insert(chest.coal, 1) ); // Thread 3 stm::atomically(ctx, extract(chest.coal, 3, chest.iron, 3) ); // Thread 4 stm::atomically(ctx, extract(chest.coal, 3, chest.copper, 3) ); // Thread 5 struct Chest { stm::TVar<int> iron; stm::TVar<int> copper; stm::TVar<int> coal; }; STM-based concurrency
  • 20.
  • 21.
    template <typename A> STML<TVar<A>>newTVar(const A& val); STM: TVar operations newTVar :: a → STML (TVar a)
  • 22.
    template <typename A> STML<TVar<A>>newTVar(const A& val); template <typename A> STML<A> readTVar(const TVar<A>& tvar); STM: TVar operations newTVar :: a → STML (TVar a) readTVar :: TVar a → STML a
  • 23.
    template <typename A> STML<TVar<A>>newTVar(const A& val); template <typename A> STML<A> readTVar(const TVar<A>& tvar); template <typename A> STML<Unit> writeTVar(const TVar<A>& tvar, const A& val); STM: TVar operations newTVar :: a → STML (TVar a) readTVar :: TVar a → STML a writeTVar :: TVar a → a → STML ()
  • 24.
    STM: Monadically composabletransactions transaction :: STML Int transaction = do tvar ← newTVar 10 readTVar tvar STML<int> transaction() { STML<TVar<int>> tvarTrans = newTVar(10); // return readTVar(tvarTrans); // ???? }
  • 25.
    STM: Monadically composabletransactions transaction :: STML Int transaction = do tvar ← newTVar 10 readTVar tvar transaction :: STML Int transaction = bind (newTVar 10) (tvar → readTVar tvar) STML<int> transaction() { STML<TVar<int>> tvarTrans = newTVar(10); // return readTVar(tvarTrans); // ???? } STML<int> transaction() { STML<TVar<int>> tvarTrans = newTVar(10); return bind(tvarTrans, [](TVar<int> tvar) { return readTVar(tvar); }); }
  • 26.
    Sample: Extract resourcestransaction STML<Unit> extract (TVar<int> resource1, int count1, TVar<int> resource2, int count2) { STML<Unit> t1 = extractResource (resource1, count1); STML<Unit> t2 = extractResource (resource2, count2); return bind (t1, [=](Unit) { return t2; }); } STML<Unit> extractResource (TVar<int> resource, int count) { STML<int> trans1 = readTVar (resource); return bind (trans1, [=](int value) { return value >= count ? writeTVar (recource, value - count) : retry(); }); }
  • 27.
    STM: C++ Interface template<typename A, typename B> STML<B> bind(const STML<A> ma, const std::function<STML<B>(A)>& f); template <typename A> STML<A> pure(const A& val); template <typename A> STML<A> retry(); template <typename A> STML<TVar<A>> newTVar(const A& val); template <typename A> STML<A> readTVar(const TVar<A>& tvar); template <typename A> STML<Unit> writeTVar(const TVar<A>& tvar, const A& val);
  • 28.
  • 29.
    Design Pattern: eDSL LanguageScenarioComposition Environment 1 Environment 2 ... Interpreting (Evaluation)
  • 30.
    template <typename A,typename Ret> struct NewTVar { A val; std::function<Ret(TVar<A>)> next; }; template <typename A, typename Ret> struct ReadTVar { TVar<A> tvar; std::function<Ret(A)> next; }; template <typename A, typename Ret> struct WriteTVar { TVar<A> tvar; A val; std::function<Ret(fp::Unit)> next; }; template <typename A, typename Ret> struct Retry { }; STM eDSL
  • 31.
    template <class Ret> structSTMF { std::variant<NewTVar <std::any, Ret>, ReadTVar <std::any, Ret>, WriteTVar <std::any, Ret>, Retry <std::any, Ret> > stmf; }; STM eDSL Generalized ADT data STMF next where NewTVar :: a -> (TVar a -> next) -> STMF next WriteTVar :: TVar a -> a -> next -> STMF next ReadTVar :: TVar a -> (a -> next) -> STMF next Retry :: STMF next
  • 32.
    template <typename A,typename B> struct StmfFunctorVisitor { STMF<B> result; void operator() (const NewTVar<std::any, STML<A>>& method); void operator() (const ReadTVar<std::any, STML<A>>& method); void operator() (const WriteTVar<std::any, STML<A>>& method); void operator() (const Retry <std::any, STML<A>>&); }; template <typename A, typename B> STMF<B> fmap(const std::function<B(A)>& f, const STMF<A>& method) { StmfFunctorVisitor<A, B> visitor(f); std::visit(visitor, method.stmf); return visitor.result; } STM eDSL ADT interpreting (“Pattern Matching”)
  • 33.
    Monadic STML chains transaction:: STML Int transaction = do tvar ← newTVar 42 v1 ← readTVar tvar v2 ← pure 10 pure (v1 + v2) STML<int> transaction() { return bind (newTVar (42), [=](TVar<int> tvar) { return bind (readTVar (tvar), [=](int v1) { return bind (pure (10), [=](int v2) { return pure (v1 + v2); }); }); }); }
  • 34.
    STML<int> ↳ STML<TVar<int>> ↳ STML<int> ↳STML<int> ↳ STML<int> Recursive STML type STML<int> transaction() { return bind (newTVar (42), [=](TVar<int> tvar) { return bind (readTVar (tvar), [=](int v1) { return bind (pure (10), [=](int v2) { return pure (v1 + v2); }); }); }); }
  • 35.
    STML<int> [](){} ↳ STML<TVar<int>> [](){} ↳ STML<int> [](){} ↳STML<int> [](){} ↳ STML<int> Recursive STML type (lambda-enclosed) STML<int> transaction() { return bind (newTVar (42), [=](TVar<int> tvar) { return bind (readTVar (tvar), [=](int v1) { return bind (pure (10), [=](int v2) { return pure (v1 + v2); }); }); }); }
  • 36.
  • 37.
    Free monad STM NewTVar WriteTVar ReadTVarRetry bind, pure STML<A> “transaction” Free ScenarioFree monad composition STM eDSL (ADT) Free monad (ADT) Interpreting Conflicts Resolving Commit or Retry
  • 38.
    Free monads -- Freemonad, normal form data Free f a = Pure a | Bind (f (Free f a)) Binding complexity: O(n2 ) <stm/free/stm.h>
  • 39.
    Free monads -- Freemonad, normal form data Free f a = Pure a | Bind (f (Free f a)) -- Free monad, Church-encoded form data ChurchFree f a = ChurchFree { runChurch :: forall z. (a -> z) -> (f z -> z) -> z } Binding complexity: O(n2 ) <stm/free/stm.h> Binding complexity: O(n) <stm/church/stm.h> (10x faster)
  • 40.
    Free monads -- Freemonad, normal form data Free f a = Pure a | Bind (f (Free f a)) -- Free monad, Church-encoded form data ChurchFree f a = ChurchFree { runChurch :: forall z. (a -> z) -> (f z -> z) -> z } -- Free monad, Scott-encoded form data Free a = Free { runFree :: forall u . (a -> u) -> (f (Free f a) -> u) -> u } Binding complexity: O(n2 ) <stm/free/stm.h> Binding complexity: O(n) <stm/church/stm.h> (10x faster) Binding complexity: O(n2 )
  • 41.
    STML Free monadtype (normal form) -- Free monad, normal form data Free f a = Pure a | Bind (f (Free f a)) -- STML Free monad type type STML a = Free STMF a
  • 42.
    STML Free monadtype (normal form) -- Free monad, normal form data Free f a = Pure a | Bind (f (Free f a)) -- STML Free monad type type STML a = Free STMF a f ~ STMF f (Free f a) ~ STMF (Free STMF a) f (Free f a) ~ STMF (STML a)
  • 43.
    STML Free monadtype (normal form) -- Free monad, normal form data Free f a = Pure a | Bind (f (Free f a)) -- STML Free monad type type STML a = Free STMF a f ~ STMF f (Free f a) ~ STMF (Free STMF a) f (Free f a) ~ STMF (STML a) template <typename Ret> struct STML { std::variant<PureF<Ret>, BindF<Ret>> stml; }; template <typename Ret> struct PureF { Ret ret; }; template <typename Ret> struct BindF { STMF<STML<Ret>> stmf; };
  • 44.
    STML Free monadtype (normal form) template <typename Ret> struct STML { std::variant<PureF<Ret>, BindF<Ret>> stml; }; template <typename Ret> struct PureF { Ret ret; }; template <typename Ret> struct BindF { STMF<STML<Ret>> stmf; }; template <class Ret> struct STMF { std::variant<NewTVar <std::any, Ret>, ReadTVar <std::any, Ret>, WriteTVar <std::any, Ret>, Retry <std::any, Ret> > stmf; }; template <typename A, typename Ret> struct NewTVar { A val; std::function<Ret(TVar<A>)> next; };
  • 45.
    STML monadic binding template<typename A, typename B> struct BindStmlVisitor; template <typename A, typename B> struct BindStmfVisitor; template <typename A, typename B> STML<B> bind(const STML<A>& stml, const std::function<STML<B>(A)>& f) { BindStmlVisitor<A, B> visitor(f); std::visit(visitor, stml.stml); return visitor.result; }
  • 46.
    Thank you for watching! AlexanderGranin graninas@gmail.com