Functor, Applicative e Monad illustrati


Introduzione

Alcuni concetti possono risultare difficilmente intuibili o sembrare troppo astratti. Functor, Applicative e Monad vi sembreranno incredibilmente semplici dopo aver letto la traduzione di questo fantastico post!

Gli esempi sono scritti in Haskell, ma i concetti sono generici e possono essere applicati in vari ambiti.

Cominciamo!

Questo è un semplice valore:

value

E sappiamo come applicare una funzione a questo valore:

value_apply

Molto semplice. Estendiamo questo concetto dicendo che un qualunque valore può trovarsi all’interno di un contesto. Per ora puoi immaginare un contesto come una scatola al cui interno puoi inserire un valore:

value_and_context

A questo punto, applicando una funzione a questo valore otterrai risultati diversi a seconda del contesto. Questa è l’idea su cui Functor, Applicative, Monad, Arrow, etc. sono basati. Il data type Maybe definisce due contesti correlati:

context

data Maybe a = Nothing | Just a

In un momento vedremo come l’applicazione di una funzione differisce quando siamo all interno di Just a piuttosto che di Nothing. Prima parliamo dei Functor!

Functor

Quando un valore è all’interno di un contesto, non puoi applicarvi una normale funzione:

no_fmap_ouch

Questo è il momento per fmap di entrare in azione. fmap sa come applicare funzioni a valori che si trovano all’interno di un contesto. Per esempio, immagina di voler applicare (+3) a Just 2. Usa fmap:

> fmap (+3) (Just 2)
Just 5

fmap_apply

Bam! fmap ci fa vedere come si fa! Ma come fa fmap a sapere come applicare la funzione?

Quindi cos’è un Functor?

Functor è una typeclass. Ecco la definizione:

functor_def

1. per rendere un data type f un functor

2. quel data type deve definire come fmap interagirà con lui

Un Functor è un qualunque data type che definisce come fmap deve essere applicato a s` stesso. Ecco come funziona fmap:

fmap_def

1. fmap prende una funzione (come (+3))

2. e un functor (come Just 2)

3. e ritorna un nuovo functor (come Just 5)

Questo ci permette di fare:

> fmap (+3) (Just 2)
Just 5

E fmap magicamente applica questa funzione, dato che Maybe e’ un Functor. Specifica come fmap si applica ai Just e ai Nothing.

 instance Functor Maybe where
    fmap func (Just val) = Just (func val)
    fmap func Nothing = Nothing

Ecco quello che succede dietro le scene quando scriviamo fmap (+3) (Just 2):

fmap_just

1. scarta il valore dal contesto

2. applica la funzione

3. reincarta il valore nel contesto

Cosa succede se chiediamo a fmap di applicare (+3) a Nothing?

fmap_nothing

1. nessun valore

2. non applicare la funzione

3. il risultato è Nothing

> fmap (+3) Nothing
Nothing

Come Morpheus in Matrix, fmap semplicemente sa cosa fare; Nothing entra e Nothing esce! fmap è zen. A questo punto l’esistenza del data type Maybe ha un senso. Per esempio, ecco come lavoreresti con un record di un database in un linguaggio che non ha Maybe:

post = Post.find_by_id(1)
if post
    return post.title
else
    return nil
end

Ma in Haskell:

fmap (getPostTitle) (findPost 1)

Se findPost trova un post, otterremo il titolo con getPostTitle. Se ritorna Nothing, il risultato finale sarà Nothing! Interessante, no? <$> &grave; la versione infix di fmap, per cui spesso vedrai la versione equivalente:

getPostTitle <$> (findPost 1)

Ecco un altro esempio: cosa succede quando applichi una funzione a una lista?

fmap_list

1. un array di valori

2. applica la funzione a ciascun valore

3. un nuovo array di valori

Anche le liste sono Functor! Ecco la definizione:

instance Functor [] where
    fmap = map

Okay, un ultimo esempio: cosa succede quando applichi una funzione ad un’altra funzione?

fmap (+3) (+3)

Questa e’ una funzione:

function_with_value

Questa è una funzione applicata ad un’altra funzione:

fmap_function

Il risultato è un’altra funzione!

> import Control.Applicative
> let foo = fmap (+3) (+2)
> foo 10
15

Quindi anche le funzioni sono Functor!

instance Functor ((->) r) where
    fmap f g = f . g

Quando usi fmap su una funzione, semplicemente stai componendo due funzioni!

Applicative

A breve…

Related Posts

Resizing and moving windows in Doom Emacs

Setting up TabNine on Doom Emacs

The correct docker-compose setup for Wordpress and Nginx

Adding a newline after a comment block in Doom Emacs

PureScript Date and Time guide

Adding live css and js reload to Yesod

A quick overview of Purescript package managers as of October 2018

Optional elements and properties in Halogen

Simple AJAX in Purescript

Automatically adding (or removing) a prefix to a record labels in Purescript