Alessio Biancalana Grab The Blaster di Alessio Biancalana

Elixir: pond, funzioni stateful e GenServer

In questi giorni mi è scorso davanti agli occhi un package interessantissimo per Elixir, ovvero pond. Pond permette di implementare quello che a conti fatti è quasi un antipattern della programmazione funzionale, ovvero funzioni che mantengano al loro interno uno stato.

All’interno del README di pond c’è una dissertazione piuttosto interessante sul fatto se convenga o meno fare uso di GenServer, ovvero di quelli che ad alto livello sono degli attori, e a basso livello sono processi, solo per mantenere lo stato di un software.

Don’t get me wrong, one of the best features of the BEAM is that it’s very cheap to create tons of processes and supervise them.

However abusing spawn, just because you want to keep state, well, that’s certainly not the smartest thing. If you created zillions of tiny processes all data between them would actually be duplicated on each message pass, since processes prefer to share nothing, messages get copied between them when sent.

Think about the Server part in GenServer, it sounds like something intended to be used by many clients something much more complex than just maintaining state.

Di seguito troviamo qualche link come “To spawn or not to spawn?” per corroborare la discussione su questo aspetto. Sinceramente tutto questo mi è stato utile per capire il ruolo dei GenServer nello sviluppo di un’applicazione Elixir (o Erlang), ed il fatto che è assolutamente vero che creare nuovi processi grazie alla BEAM è molto semplice, ma la creazione di nuovi processi dovrebbe essere qualcosa che viene dall’architettura dell’applicazione che stiamo progettando, e non tanto dalla volontà di cavarsela con poco nella gestione di uno o due staterelli.

Come ci si sente a fare lo speaker al Facebook Developer Circle

A quasi un mese di distanza, mi sembra giusto parlare del fatto che sono stato speaker in un meetup: lo scorso Facebook Developer Circle di Roma. Strano che non ne abbia parlato prima, ho come buttato la cosa nel dimenticatoio, ed è strano perché invece sono veramente fiero di com’è andata, del tema che ho trattato, delle tecnologie che ho usato per il mio proof of concept.

Generalmente i meetup che frequento stabilmente sono due: RomaJS, di cui sono un affezionatissimo, ed Elixir Roma, a cui ormai lavorando con Claudio e facendogli da vassallo nell’organizzazione sono legato per la vita. (:-D)

Nel corso del tempo mi sono accorto di aver bisogno di sgranchirmi come speaker, così quando Innocenzo e Giovanni hanno deciso di cooptarmi per farmi diventare il loro uomo della serata - insieme a degli altri eccezionali compari - sono stato ben lieto di dirgli si in modo da vedere se potevo cavarmela di fronte a quel tipo di audience o se a ventisette anni devo già relegarmi tra i grandi successi del secolo scorso. A proposito, grazie per lo speaker pack1 ragazzi!

Facebook Developer Circle: Rome - Alessio Biancalana

E invece è andata bene: l’argomento che ho deciso di trattare è stata la creazione di un chatbot sulla piattaforma di Facebook Messenger in pochissimo tempo. Per scrivere il bot mi sono destreggiato con Elixir in una giornata natalizia, e devo scrivere che per tanti aspetti questo ha contribuito a farmi credere ancora di più che Elixir sia adatto sia per codebase piccole che per grandi monoliti. In foto mi vedete ritratto da Andrea Millozzi mentre cerco di commentare una mappa che non è uscita fuori esattamente nel modo che mi aspettavo, e che rappresenta una risposta dei server di Facebook a un evento di messaggistica arricchito con dei dati relativi al Natural Language Processing del messaggio.

Se non ci metto del mio in termini di idiozia non sono mai contento, così ho inserito (dato il clima delle festività passate) un pezzettino di logica ulteriore che facendo leva sulle meccaniche dell’NLP offerto da Facebook, in caso di auguri, risponda con “a te e famiglia”. Utile per il prossimo Natale, no?

Facebook Developer Circle: Rome - gente con la maglietta di dottorblaster addosso

E (lo so che non si iniziano i periodi con “e”, ci sono andato a scuola, ma il libero arbitrio prevede anche fottersene delle regole dei grandi) se tutto questo non basta per fare una grande serata, a quanto pare entrando in sala ho scoperto di avere un fan club. A quanto pare l’insanità mentale che mi porto dietro ha deciso di estendere i suoi tentacoli ai miei amici, che si sono fatti fare di nascosto delle magliette che potete vedere in diapositiva, per poi sfoggiarle la sera del meetup. Giusto per farmi rilassare quei cinque minuti prima di parlare, senza mettermi pressione addosso.

Il lato tecnologico

Facebook permette di mettere in piedi un bot in pochissimo tempo, basta creare un’applicazione dal developer portal, legarla a una fanpage, dopo di che abilitare la piattaforma Messenger e utilizzare il token che ci viene fornito. In aggiunta a questo c’è il webhook da configurare. Il mio talk ha parlato di questo, detto in estrema sintesi.

Quello su cui voglio concentrarmi adesso invece è qualcosa di leggermente diverso, ovvero la codebase del bot, scritta in Elixir, che potete trovare su GitHub. Ho deciso di scrivere il bot in Elixir per due ordini di motivi:

  • Avere alla fine del processo di sviluppo una demo da portare al talk che non fosse un copincolla della documentazione di Facebook, che pur fatta benissimo è molto incentrata su NodeJS;
  • Mettere alla prova Elixir per scrivere una codebase semplice che però avesse una serie di caratteristiche.

Le feature di Elixir che ho apprezzato rispetto alla scrittura di un bot sono state i module attributes, che ho arricchito facendo arrivare il valore di un attributo direttamente dalla configurazione del bot, e la possibilità di implementare un client HTTP per i propri servizi in un tempo praticamente nullo grazie ad HTTPoison, con un semplice use.

A dire il vero volevo fare qualcosa di più, e pubblicare un client fatto decentemente per la Messenger API direttamente su Hex, ma non ci sono riuscito per mancanza di tempo.

Conclusioni

Dovremmo sempre dire dei “sì” che non possiamo permetterci di dire. Da questo sì, detto in un momento in cui ero pieno di lavoro e codice da scrivere (e non solo), ho ricavato il superamento di un limite, un bel talk, un bel software, e tantissimi amici che mi hanno fatto una delle sorprese più belle di sempre.

  1. Lo speaker pack comprende una maglietta ufficiale del Facebook Developer Circle, una penna, un’agenda e non mi ricordo più che altro. La maglietta è fica, davvero - come tutto il resto d’altronde. Ma la maglietta un po’ di più. 

Elixir per idioti /3, Enum e funzioni di Enum

Andando in ordine sparso rispetto alle funzioni messe a disposizione dalla standard library di Elixir, una cosa che adoro letteralmente è il modulo Enum.

Il protocollo Enumerable

La prendo da lontano: il modulo Enum contiene funzioni che posso iterare su qualsiasi tipo, custom e non, che implementi il protocollo Enumerable. Per implementare un protocollo la documentazione ci mette a disposizione tutto ciò che dobbiamo sapere, e ci basta implementare le funzioni che quello specifico protocollo richiede.

Io sono un fan dell’approccio configuration-over-convention, ma in questo caso devo dire che pur con le mie perplessità penso che questo tipo di convention-over-configuration sia il meglio che si possa ottenere senza rischiare di fare casino.

Con convention-over-configuration si fa sempre casino ndr.

Per tantissimi tipi di base, il protocollo Enumerable è già implementato di suo e non dobbiamo assolutamente preoccuparci di scrivere noi le funzioni che consentano al modulo Enum di operare indisturbato; questo significa che la premessa di cui sopra per i non smaliziati è assolutamente inutile, però mi piaceva farla.

E ora, dai con le cose importanti.

Enum

Il modulo Enum serve a fare pressoché qualsiasi operazione di cui abbiamo bisogno su tipi iterabili, come le liste o le mappe. Ci espone un sacco di funzioni, di cui le più importanti “ovviamente” (ovviamente un corno, ma per chi conosce la programmazione funzionale è ovvio) sono map e reduce.

Un esempio:

iex(1)> list = [1, 2, 56, 43, 90]
[1, 2, 56, 43, 90]
iex(2)> Enum.map(list, fn(x) -> x * 2 end)
[2, 4, 112, 86, 180]

Abbiamo dichiarato una lista con dei valori dentro, dopodiché abbiamo mappato la funzione che raddoppia il valore che le diamo in pasto su ogni elemento della lista che abbiamo dichiarato in precedenza. In questo caso abbiamo usato una funzione anonima (ovvero una funzione dichiarata inline a cui non abbiamo dato un nome).

Abbiamo mille altre funzioni per ottenere informazioni sulla nostra lista, come count:

iex(3)> Enum.count(list)
5

O per manipolarla:

iex(4)> Enum.concat(list, [23, 42])
[1, 2, 56, 43, 90, 23, 42]
iex(5)> list
[1, 2, 56, 43, 90]

Notiamo bene che in perfetta linea con l’orientamento funzionale del linguaggio, queste funzioni non modificano il parametro che passiamo in ingresso, ma per esempio per quanto riguarda la nostra lista ne ritornano una nuova. Per mantenere il risultato della nostra concat, dobbiamo quindi dichiarare una nuova lista, e in questo modo potremo fare logica sull’output della funzione.

iex(6)> larger_list = Enum.concat(list, [23, 42])
[1, 2, 56, 43, 90, 23, 42]
iex(7)> larger_list
[1, 2, 56, 43, 90, 23, 42]
iex(8)> Enum.map(larger_list, fn(x) -> x * 3 end)
[3, 6, 168, 129, 270, 69, 126]

Il manuale di Enum è piuttosto vasto. Ve l’ho già detto che userei il manuale di Elixir in generale come lettura della buona notte? La reference di Enum la troviamo qui.

È importante notare come le funzioni, anonime o meno che siano, che passiamo alle funzioni di Enum, possano cambiare leggermente la loro API in funzione del tipo su cui iteriamo. Per esempio per iterare su una mappa abbiamo bisogno di una funzione con ingresso un parametro, una tupla con la chiave e il valore, su cui dentro la funzione che dichiariamo potremo fare logica ovviamente.

iex(9)> map_to_iterate = %{top: 1, lol: "this is a random string"}
%{lol: "this is a random string", top: 1}
iex(10)> Enum.map(map_to_iterate, fn(param) -> IO.inspect(param) end)
{:lol, "this is a random string"}
{:top, 1}
[lol: "this is a random string", top: 1]

In questo caso non abbiamo modificato nulla per cui la funzione ci ha stampato quello che le abbiamo chiesto di stampare (ovvero la tupla in input), e ci ha restituito la mappa convertita in una lista di tuple, che si può consultare nello stesso modo di una mappa standard quindi non ci crea problemi.

Possiamo comunque anche modificare la nostra mappa in ingresso:

iex(11)> Enum.map(map_to_iterate, fn({k, v}) -> {k, "Look! #{v}"} end)
[lol: "Look! this is a random string", top: "Look! 1"]

Ma che cosa abbiamo appena fatto? Abbiamo usato una sintassi molto concisa per dichiarare il valore di ritorno della nostra funzione. Probabilmente ne parleremo di qui a breve: Elixir consente una potenza espressiva notevole, il che significa che in pochissime righe di codice possiamo fare parecchio. Questo significa anche, però, che ad un occhio meno allenato il codice possa risultare meno comprensibile.

Vai alla parte 2

Non puoi piacere a tutti

Non c’entra molto col tema principale di questo blog ma Raffaele Gaito è una di quelle persone che mi fregio di conoscere da un bel po’ di tempo. Lui ha avuto il suo percorso, io ho avuto il mio, e quando parlo con lui è sempre un piacere confrontarsi, specie perché magari avendo costantemente il pregiudizio causato dal fatto di avere a che fare principalmente con persone orientate alle professioni IT, Raffaele è esattamente quel tipo di persona di cui ho bisogno per riferirmi a lui come un buon punto di vista esterno.

E poi consiglia delle pizze da paura!

Ieri ha pubblicato il suo “7 cose (di business) che ho imparato nel 2017”, e leggendolo ho notato questo:

Poi a un certo punto ti trovi di fronte a un bivio: o impazzisci per stare dietro all’opinione delle persone o realizzi che non puoi piacere a tutti. Passi in modalità “sticazzi, chi mi ama mi segua” e ti accorgi che da un certo punto è inevitabile che il pubblico si spacchi, che la loro opinione diventi polarizzata e che starai antipatico a qualcuno.

Appena l’ho realizzato è stato come aver scoperto le altre marce di una Aston Martin che stavo guidando in prima da troppo tempo.

È vero. Mi ha fatto particolarmente bene leggerlo, perché sono tesissimo dato che domani parlo al Facebook Developer Circle di Roma e sto ancora lavorando alle slide in maniera ossessiva. Mi ha tranquillizzato.

Grazie Raf. 🚀

Elixir 1.6

Elixir 1.6 è fuori, into the wild.

Novità rilevanti:

  • Formatter! Yay!
  • @deprecated e @since per favorire le transizioni tra versioni “breaking” dei moduli
  • IEx adesso integra il formatter e una serie di nuove feature per fare pretty print degli snippet
  • mix xref dà informazioni superfiche su progetto corrente e dipendenze
  • defguard? Le guardie sono un concetto che non ho approfondito, ma potentissime. Adesso possiamo definirle e usarle nella firma delle funzioni. Non capisco bene quanto di questo differisca dal passato.

Per approfondimenti, il post originale è al suo posto (ed è linkato nell’header, come sempre). E ovviamente il manuale!