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

React-router-dom bindings for Reason

Parsing complex foreign objects in PureScript

A way to deal with big objects and FFI.

Post requests with PureScript Affjax and Argonaut

Simple ajax calls to an API example

Arbitrary length lists with QuickCheck

Using sized to build arbitrary length lists for QuickCheck

PureScript Impressions

Trying to put some sense in JavaScript

Clojure Examples

Toggling DELL 9350's touchpad on Ubuntu 15.10

How to set up a hotkey to toggle DELL 9350's touchpad on Ubuntu 15.10

Using the Asus MB168B+ with Ubuntu 15.10

How to edit the driver install script to make it work on Ubuntu 15.10+ (and other distros)