Quale problema risolvono i tipi di dati algebrici?

18

Giusto avviso, sono nuovo alla programmazione funzionale, quindi potrei contenere molte ipotesi sbagliate.

Ho imparato a conoscere i tipi algebrici. Molte lingue funzionali sembrano averle e sono abbastanza utili in combinazione con la corrispondenza dei modelli. Tuttavia, quale problema risolvono effettivamente? Posso implementare un tipo algebrico apparentemente (specie di) in C # come questo:

public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
    public T Value { get; set; }
}

var result = GetSomeValue();
if(result is None)
{
}
else
{
}

Ma penso che la maggior parte sarebbe d'accordo che questa è una bastardizzazione della programmazione orientata agli oggetti, e non dovresti mai farlo. Quindi la programmazione funzionale aggiunge solo una sintassi più pulita che rende questo stile di programmazione meno evidente? Cos'altro mi manca?

    
posta ConditionRacer 21.06.2015 - 20:00
fonte

3 risposte

44

Le classi con interfacce e eredità presentano un mondo aperto: chiunque può aggiungere un nuovo tipo di dati. Per una determinata interfaccia, potrebbero esserci classi che la implementano in tutto il mondo, in diversi file, in diversi progetti, in diverse aziende. Rendono facile aggiungere casi alle strutture dati, ma poiché le implementazioni dell'interfaccia sono decentralizzate, è difficile aggiungere un nuovo metodo all'interfaccia. Una volta che un'interfaccia è pubblica, è fondamentalmente congelata. Nessuno conosce tutte le possibili implementazioni.

I tipi di dati algebrici sono il doppio, sono chiusi . Tutti i casi dei dati sono elencati in un unico posto e le operazioni non solo possono elencare le varianti in modo esaustivo, sono incoraggiati a farlo. Di conseguenza scrivere una nuova funzione che funziona su un tipo di dati algebrico è banale: basta scrivere la maledetta funzione. In cambio, l'aggiunta di nuovi casi è complicata perché devi basare praticamente l'intero codice base ed estendere ogni match . Simile alla situazione con le interfacce, nella libreria standard di Rust, l'aggiunta di una nuova variante è una modifica irrisolta (per i tipi pubblici).

Questi sono due lati del problema di espressione . I tipi di dati algebrici sono una soluzione incompleta per loro, ma lo è anche per OOP. Entrambi presentano vantaggi in base al numero di casi di dati presenti, alla frequenza di tali casi e alla frequenza con cui le operazioni vengono estese o modificate. (Questo è il motivo per cui molti linguaggi moderni forniscono entrambi, o qualcosa di simile, o vanno dritti per meccanismi più potenti e più complicati che tentano di sopprimere entrambi gli approcci.)

    
risposta data 21.06.2015 - 20:27
fonte
12

So does functional programming just add a cleaner syntax that makes this style of programming seem less gross?

Forse è una semplificazione, ma sì.

What else am I missing?

Siamo chiari su quali tipi di dati algebrici sono (riassumendo questo bel link di Learn you as Haskell):

  • Un tipo di somma che dice "questo valore può essere A o a B".
  • Un tipo di prodotto che dice "questo valore è sia un che a B".

il tuo esempio funziona solo con il primo.

Quello che forse ti manca è che, fornendo queste due operazioni di base, i linguaggi funzionali ti consentono di costruire tutto il resto. C # ha structs, classes, enum, generici in cima a quelli e pile di regole per governare come queste cose si comportano.

Combinato con una certa sintassi per aiutare, i linguaggi funzionali possono scomporre le operazioni su questi due percorsi, fornendo un approccio pulito, semplice ed elegante ai tipi.

What problem do algebraic data types solve?

Risolvono lo stesso problema di qualsiasi altro tipo di sistema: "quali valori sono legali da usare qui?" - hanno solo un approccio diverso.

    
risposta data 21.06.2015 - 20:34
fonte
6

Potrebbe sorprendervi sapere che l'abbinamento dei pattern non è considerato il modo più idiomatico per lavorare con Options. Consulta la documentazione delle opzioni di Scala per ulteriori informazioni al riguardo. Non sono sicuro del motivo per cui così tante esercitazioni FP incoraggiano questo utilizzo.

Per lo più quello che ti manca è che ci sono un sacco di funzioni create per semplificare il lavoro con le Opzioni. Considera l'esempio principale dei documenti Scala:

val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")

Nota come map e filter ti consentono di concatenare le operazioni su Opzioni, senza dover controllare in ogni punto se hai None o meno. Quindi, alla fine, si utilizza getOrElse per specificare un valore predefinito. In nessun momento stai facendo qualcosa di "grossolano" come i tipi di controllo. Qualsiasi controllo di tipo inevitabile viene eseguito internamente alla libreria. Haskell ha il suo proprio set di funzioni analogiche, per non parlare del grande serie di funzioni che funzioneranno su qualsiasi monade o funtore.

Altri tipi di dati algebrici hanno i loro modi idiomatici per lavorare con loro, e la corrispondenza del modello è bassa sul totem nella maggior parte dei casi. Allo stesso modo, quando crei i tuoi tipi, devi fornire funzioni simili per lavorare con loro.

    
risposta data 22.06.2015 - 19:00
fonte

Leggi altre domande sui tag