Come progettare astrazioni buone usando il tipo di dati algebrico?

5

Ogni tanto ho raggiunto il picco di Haskell Tutorials e ho trovato i tipi di dati Algebraic piuttosto interessanti. Ho preso lo scopo di rappresentare i tipi che hanno stati completamente separabili. Purtroppo non ho mai scritto più Haskell di progetti a livello di tutorial, quindi non ho mai dovuto progettare programmi usando questo pattern.

Ora sto scrivendo un po 'di ruggine e ho dei tipi di dati algebrici (enumerati) nella casella degli strumenti. Tuttavia, non sono molto fiducioso nel usarli.

Vorrei iniziare con un esempio in cui sono fiducioso che tale enum è una scelta appropriata.

enum Tree {
    Leaf(i: String),
    Branch(Tree, Tree)
}

Lo stesso esempio sarebbe applicaple per una struttura simile a XML, ecc.

Con altri dati, non sono così sicuro di usare i tipi di enumerazione. Prendiamo un oggetto di connessione

enum Connection {
    UnConnected(...),
    ConnectedConnection(....)
}

Qui avremmo un tipo di connessione con due valori possibili, uno che rappresenta lo stato in cui una connessione non è ancora stata stabilita, l'altro potrebbe rappresentare lo stato di una connessione connessa (e avvolgere un handle di connessione per esempio).

L'altra possibilità sarebbe quella di introdurre 2 tipi per un modello di connessione e una connessione connessa.

Un altro esempio che ho trovato nel codice ruggine è quello nella libreria hyper . Esiste il tipo Response che rappresenta una risposta HTTP. È un tipo generico. Response<Fresh> rappresenta lo stato in cui le intestazioni non sono ancora state congelate. Una volta che è mappato su un Response<Streaming> , che può essere utilizzato per scrivere il corpo della risposta. Sembra che i modelli Hyper qui con tipi diversi ( Response<Fresh> vs Response<Streaming> ) avrebbero potuto essere modellati anche con enum .

L'approccio con tipi diversi consente maggiore sicurezza, Response<Fresh> non implementa lo streaming (credo) e Response<Streamimg> lo fa.

Conosci le linee guida e le best practice che guidano correttamente la logica di modellazione nei tipi?

    
posta wirrbel 23.05.2015 - 21:35
fonte

2 risposte

5

Anche se hai detto tipi di dati algebrici, sembra che tu stia principalmente chiedendo dei tipi di somma, quindi mi concentrerò su quelli. I tipi di prodotto sono più comuni e più facilmente comprensibili.

I tipi di somma sono più facilmente comprensibili non pensando a cosa stai modellando, ma pensando al codice che usa . Le persone tendono a pensare ai tipi di somma come a rappresentare stati che si escludono a vicenda, ma questo non è l'intero quadro. Per utilizzare un tipo di somma, si dovrebbe anche avere un uso per il tipo che comprende. Questo rappresenta uno stato indeterminato, dove al punto di chiamare la funzione, sai che hai o hai bisogno di uno dei tipi di termine, ma non sai quale. Se sai sempre quale staticamente, dovresti semplicemente creare tipi separati.

Per il tuo esempio di Tree , ciò significa che devi avere funzioni che effettivamente prendono o restituiscono un Tree . Mentre attraversi l'albero, hai questo stato in cui non sai se il prossimo nodo verso il basso sarà un Leaf o un Branch , quindi hai bisogno di un tipo che possa essere entrambi. Se hai solo funzioni che accettano o restituiscono Leaf s o Branch es, non ti serve un tipo di somma.

Il tuo esempio di Connection è una questione completamente diversa. Ad un certo punto, hai una funzione connect che contiene un Unconnected e restituisce un Connected . In un secondo momento, hai una funzione disconnect che fa il contrario. Non esiste un punto in cui non si conosce in modo statico se una connessione è connessa o meno, quindi non è necessario un tipo che possa contenere entrambi.

Se hai difficoltà a vedere le situazioni appropriate per utilizzare i tipi di somma, la mia raccomandazione è di iniziare non usandoli. Se sono appropriati, ad un certo punto ti verrà assegnata una funzione che non può essere scritta altrimenti, quindi puoi aggiungerla. Forzare un tipo di somma quando non è necessario porta a un sacco di combinazioni di pattern non necessarie che potrebbero essere molto più pulite con funzioni separate.

    
risposta data 24.05.2015 - 15:47
fonte
4

Do you know of guidelines and best practices that guide through modelling logic in types properly?

Certo, almeno in teoria.

I tipi di dati algebrici sono composti da due parti, tipi di somma (o varianti o unioni discriminate o ...) e tipi di prodotto (o tuple; e le classi cadono qui, ma alcuni non sono d'accordo).

I tipi di prodotto vengono utilizzati quando hai bisogno di e . Hai bisogno di un nome utente e una password? Crea un tipo di prodotto di una stringa e un'altra stringa. Hai bisogno di una data e un orario? Crea un tipo di prodotto che combini i due. Le tuple sono ordinate in coppie di cose, i record e le classi sono non ordinate ma accessibili per nome, ma il concetto è lo stesso del design.

I tipi di somma vengono utilizzati quando hai bisogno di o . Hai bisogno di una foglia o di un albero completo? Tipo di somma Hai bisogno di un flusso di dati o un errore? Somma il tipo.

Quindi, quando modellizzi quale tipo di dati è legale per / da una funzione o in una variabile, tieni a mente questi concetti. Se hai solo bisogno di un certo valore, usa un semplice vecchio tipo. Se hai bisogno di un valore e un altro? Tipologia di prodotto. Se puoi avere un valore o un altro (ma non entrambi)? Sum Type. Qualche combinazione (come l'albero sopra)? Alcune combinazioni.

Le migliori pratiche variano da una lingua all'altra. Molte lingue non rendono i Sum Type prontamente disponibili (oltre i valori nullable), o scomodi da usare. Molte lingue preferiscono utilizzare le raccolte predefinite per tipi di prodotti semplici (perché sono ben ottimizzati). Ma i concetti restano in gran parte una volta che impari come la tua lingua vuole che tu li codifichi.

    
risposta data 24.05.2015 - 01:07
fonte

Leggi altre domande sui tag