Por fin entiendo qué son las mónadas - using std::cpp · Por fin entiendo qué son las mónadas ....
Transcript of Por fin entiendo qué son las mónadas - using std::cpp · Por fin entiendo qué son las mónadas ....
Telefónica Digital – Video Products Definition & Strategy
using std::cpp 2014
Joaquín Mª López Muñoz <[email protected]> Madrid, octubre 2014
Por fin entiendo qué son las mónadas
No es esto…
Tampoco esto…
¡Son las mónadas!
Definición formal
Definición formal
Pero, ¿qué es realmente una mónada?
Una cinta transportadora
Un overload del operador “;”
Una unidad de computación
Un truco para introducir efectos
laterales en programación
funcional
Un objeto cuyos métodos
devuelven mónadas
No es posible definir qué es una mónada
Algo parecido a un escritorio
Una forma de componer funciones
Construyamos una mónada en C++
optional<T>
template<typename T> struct optional { optional(T const& x) optional(none_t); T const& get()const; T& get(); operator bool()const; // not really }; optional<double> inv(double x){ if(x==0.0)return none; else return 1.0/x; } optional<double> sqr(double x){ if(x<0.0)return none; else return std::sqrt(x); } optional<double> arcsin(double x){ if(x<-1.0||x>1.0)return none; else return std::asin(x); }
Calcular…
La composición directa de funciones no compila optional<double> f(double x) { return inv(arcsin(sqr(x)); } main.cpp: In function 'boost::optional<double> f(double)': main.cpp:34:27: error: cannot convert 'boost::optional<double>' to 'double' for argument '1' to 'boost::optional<double> arcsin(double)' return inv(arcsin(sqr(x)); ^
arcsin acepta doubles, no optional<double>s
Semántica deseada: none_t aborta la computación
𝒇 𝒙 =𝟏
arcsin( 𝒙)
Ahora sí
optional<double> f(double x) { auto y=sqr(x); auto z=y?arcsin(y.get()):none; auto w=z?inv(z.get()):none; return w; }
¿Detectas un patrón aquí?
Hagámoslo genérico
template<typename T,typename F> auto operator>>=(const optional<T>& x,F&& f) { return x?f(x.get()):none; } optional<double> f(double x) { return (sqr(x)>>=arcsin)>>=inv; }
No hay nada especial en la elección de “>>=”
En C++, de hecho, no es muy buena elección
Léase bind (tampoco muy afortunado en C++)
optional<double> f(double x) { return inv(arcsin(sqr(x)); } optional<double> f(double x) { return (sqr(x)>>=arcsin)>>=inv; }
>>= es un adaptador activo
x arcsin inv
>>= sqr x
arcsin
>>=
inv
sqr
optional<double> f(double x) { return ((optional<double>(x)>>=sqr)>>=arcsin)>>=inv; }
unit acepta un objeto x y devuelve la mónada asociada a x
También se lo llama return (confuso en C++)
En este ejemplo unit(x) ~ optional<double>(x)
Un poco de Lego
>>= x
arcsin
>>=
inv
>>=
sqr
unit
optional<double> f(double x) { auto g=[](double y){return arcsin(y)>>=inv;}; return sqr(x)>>=g; }
bind es “asociativo”
Un poco de Lego
>>= sqr x
arcsin
inv
>>=
optional<double> f(double x) { auto minv=[](const optional<double>& y){return y>>=inv;}; auto marcsin=[](const optional<double>& y){return y>>=arcsin;}; auto msqr=[](const optional<double>& y){return y>>=sqr;}; return minv(marcsin(msqr(optional<double>(x)))); }
Por cierto, esta mónada es comúnmente conocida como Maybe Monad
Un poco de Lego
sqr
>>=
arcsin
>>=
inv
>>=
x unit
Las leyes de la mónada
unit : T M<T>
>>= : (M<T>, T M<T’>) M<T’>
unit(x) >>= f ≡ f(x)
m >>= unit ≡ m
(m >>= f) >>= g ≡ m >>= λx.(f(x) >>= g)
¿Qué es una mónada? Mi modesto intento de definición
Una mónada es un patrón de diseño que permite componer funciones con tipos de retorno extendidos
Un tipo extendido contiene 0 ó más valores de un tipo básico más cierta semántica/información asociada
¿Qué es una mónada? Mi modesto intento de definición
template<typename T> class histogram { public: histogram(){} histogram(const T& x); // our unit ctor, later on const_iterator begin()const; const_iterator end()const; void add(const T& x,double f); }; histogram<int> dice(int n) { histogram<int> res; for(int i=1;i<=n;++i)res.add(i,1.0/n); return res; } int main() { std::cout<<dice(6); }
Algo más complicado: histogramas
1 ******************** 2 ******************** 3 ******************** 4 ******************** 5 ******************** 6 ********************
Composición de histogramas ~ probabilidad condicionada
1
0.2
2
0.4
3
0.4
A
0.2
B
0.3
C
0.5
A
0.0
B
0.6
C
0.4
A
0.25
B
0.5
C
0.25
A
0.14
B
0.5
C
0.36
template<typename T,typename F> auto operator>>=(const histogram<T>& x,F&& f) { decltype(f(x.begin()->first)) res; for(const auto& p:x){ for(const auto& q:f(p.first)){ res.add(q.first,q.second*p.second); } } return res; } int main() { auto h=dice(6)>>=dice; std::cout<<h; }
Estrictamente hablando, histogram<T> es una mónada
restringida
Composición de histogramas ~ >>=
1 ************************************************* 2 **************************** 3 ******************* 4 ************ 5 ******* 6 ***
int main() { auto h1=dice(6); auto h2=dice(4); auto h3= h1>>=[&](int x){ return h2>>=[&](int y){ return histogram<int>(x+y); }; }; std::cout<<h3; }
En Haskell, este constructo se implementa con la notación do do x <- dice 6 y <- dice 4 return x+y
No tan relevante en un lenguaje imperativo como C++ (por ahora)
Pero hay otro concepto más interesante aquí…
Suma de experimentos
2 ***** 3 ********** 4 *************** 5 ******************** 6 ******************** 7 ******************** 8 *************** 9 ********** 10 *****
Lifting monádico
f(T1, … ,Tn) R F(M<T1>, … , M<Tn>) M<R> template<template<typename> class M,typename T1,typename T2> auto operator+(const M<T1>& m1,const M<T2>& m2) { return m1>>=[&](const T1& x){ return m2>>=[&](const T2& y){ return M<decltype(x+y)>(x+y); }; }; } int main() { auto h1=dice(6); auto h2=dice(4); auto h3=h1+h2; std::cout<<h3; std::cout<<"--------------------------\n"; std::cout<<optional<int>(4)+optional<int>(3)<<"\n"; std::cout<<optional<int>(4)+optional<int>(none)<<"\n"; }
Lifting monádico
2 ***** 3 ********** 4 *************** 5 ******************** 6 ******************** 7 ******************** 8 *************** 9 ********** 10 ***** -------------------------- 7 none
Intersección de líneas captura / closure
Lifting monádico con argumentos variádicos: http://tinyurl.com/mlifting
La implementación no es trivial
Diagrama de flujo
>>=
+ unit
m2
>>= m1
Un catálogo de mónadas
Un catálogo de mónadas
Maybe
List
I/O
State
Reader
Writer
Continuation
Continuation Monad (muy por encima)
Construcción de un hilo
template<typename T,typename R> struct yarn { yarn(const T& x); template<typename F> // F: T->T2 yarn<T2,R> then(F f)const; R run(); }; int str_size(const std::string& str); int times_10(int x); std::string to_str (int x); int main() { auto c1=yarn<std::string,int>("hello"). then(str_size). then(times_10). then(to_str). then(str_size); std::cout<<"running...\n"; std::cout<<c1.run()<<"\n"; }
str_size, times_10, to_str no devuelven valores monádicos
De hecho, then se implementa en función de >>=
¿Tanto lío para llamar unas cuantas funciones una tras otra?
El esquema es familiar, ¿no?
then hello then then
times_10 str_size to_str
then
str_size
then >>=
f f
yarn
yarn
Unas notas antes de partir
Unas notas antes de partir
Es posible entender las mónadas
¡Veo mónadas por todas partes!
Patrón de diseño para la composición de funciones extendidas
Haskell: mónadas + do estilo imperativo
C++: inversión de control, entornos no imperativos
Lifting remplazamiento de valores básicos por valores monádicos
Louis, creo que esto puede ser el comienzo de una bella amistad
Por fin entiendo qué son las mónadas
Gracias
github.com/joaquintides/usingstdcpp2014
using std::cpp 2014
Joaquín Mª López Muñoz <[email protected]> Madrid, octubre 2014