Qual è il significato di "non comporre"?


35

Vedo molti testi, in particolare testi di programmazione funzionale, che affermano che alcuni concetti di CS "non compongono" . Esempi sono: i blocchi non si compongono, le monadi non si compongono.

Ho avuto difficoltà a rintracciare esattamente il significato di questa frase. Quando penso alla composizione, penso sia alla composizione della funzione che all'aggregazione di oggetti (come nel "favorire la composizione sull'eredità"), ma questo non sembra essere il senso in cui le persone lo usano qui.

Qualcuno può spiegare cosa significa questa frase quando viene usata in espressioni come i due esempi sopra (cioè, blocchi e monadi)?


Nel significato della composizione è più vicino che nell'aggregazione degli oggetti.
Andres F.

In modo approssimativo e informale, se hai due cose separate che usano i lucchetti, è difficile metterli insieme. (nel caso di blocchi, è difficile fare a meno di introdurre deadlock; nel caso di monadi, i tipi possono complicarsi)
user253751

Risposte:


35

Quando le persone dicono che "X non compone", ciò che intendono per "comporre" significa in realtà "mettere insieme", e cosa e come li metti insieme può essere molto diverso, a seconda di cosa sia esattamente "X".

Inoltre, quando dicono "non comporre", possono significare alcune cose leggermente diverse:

  1. Non puoi mettere insieme due X, punto.
  2. È possibile mettere due Xs insieme, ma il risultato potrebbe non essere una X (IOW: X non è chiuso sotto la composizione .)
  3. Puoi mettere insieme due X, ma la X risultante potrebbe non funzionare come previsto.

Un esempio per il n. 1 sono i parser con scanner / lexer. Potresti sentire la frase "scanner / lexer non compongono". Questo non è in realtà vero. Ciò che intendono è "il parser che utilizza una fase di lexing separata non compone".

Perché dovresti voler comporre parser? Bene, immagina di essere un fornitore IDE come JetBrains, Eclipse Foundation, Microsoft o Embarcadero e vuoi creare un IDE per un framework web. Nello sviluppo web tipico, mescoliamo spesso le lingue. Hai file HTML con <script>elementi contenenti ECMAScript e<style>elementi contenenti CSS. Sono disponibili file modello contenenti HTML, alcuni linguaggi di programmazione e alcune metasintassi del linguaggio dei modelli. Non vuoi scrivere diversi evidenziatori di sintassi per "Python", "Python incorporato in un modello", "CSS", "CSS in HTML", "ECMASCript", "ECMAScript in HTML", "HTML", "HTML entro un modello ", e così via e così via. Si desidera scrivere un evidenziatore di sintassi per Python, uno per HTML, uno per il linguaggio modello e quindi comporre i tre in un evidenziatore di sintassi per un file modello.

Tuttavia, un lexer analizza l'intero file in un flusso di token, il che ha senso solo per quella lingua. Il parser per l'altra lingua non può funzionare con i token che il lexer gli passa. Ad esempio, i parser Python sono in genere scritti in modo tale che il lexer tenga traccia dell'indentazione e inietti falsi INDENTe DEDENTtoken nel flusso di token, consentendo così al parser di essere privo di contesto, anche se in realtà la sintassi di Python non lo è. Un lexer HTML tuttavia ignorerà completamente gli spazi bianchi, poiché non ha alcun significato in HTML.

Un parser senza scanner, tuttavia, che legge semplicemente i caratteri, può passare il flusso di caratteri a un altro parser, che può quindi restituirlo, rendendoli così molto più facili da comporre.

Un esempio per # 2 sono le stringhe con query SQL al loro interno. È possibile avere due stringhe, ognuna delle quali contiene una query SQL sintatticamente corretta, ma se si concatenano le due stringhe, il risultato potrebbe non essere una query SQL sintatticamente corretta. È per questo che abbiamo di query algebre come ARel, che fanno di composizione.

I blocchi sono un esempio di # 3. Se hai due programmi con blocchi e li combini in un singolo programma, hai ancora un programma con blocchi, ma anche se i due programmi originali erano completamente corretti, privi di deadlock e razze, il programma risultante non ha necessariamente questo proprietà. L'uso corretto dei blocchi è una proprietà globale dell'intero programma e una proprietà che non viene preservata quando si compongono i programmi. Questo è diverso da, per esempio, le transazioni, che fanno di composizione. Un programma che utilizza correttamente le transazioni può essere composto con un altro programma simile e produrrà un programma combinato che utilizza correttamente le transazioni.


1
In altre parole, "non compone" significa "non forma un semigruppo" (o, leggermente più fortemente, un monoide).
Jon Purdy,

Non sono sicuro che sia necessaria l'associatività, quindi più simile a un Magma (di cui ho imparato letteralmente 3 secondi fa MrGreen)
Jörg W Mittag,

Tuttavia, se chiedi a qualcuno cosa significano quando dicono "i blocchi non compongono", sono abbastanza sicuro che non risponderanno "Voglio dire che l'insieme di programmi simultanei corretti con i blocchi e l'operazione di composizione formano un Magma" .
Jörg W Mittag,

Ah, certo che no. Solo un fraseggio alternativo. Ho la sensazione che sia necessaria l'associatività per mantenere il sentimento "piatto" della composizione, ma nessuna argomentazione forte per sostenerlo.
Jon Purdy,

20

Composibilità significa che è possibile combinare facilmente e in modo affidabile componenti del programma per produrre componenti più grandi e funzionalità più complesse.

Alcune cose che aiutano a rendere i componenti più compostabili:

  1. Idempotence. Una funzione idempotente produrrà sempre lo stesso output o gli stessi effetti collaterali, se chiamata più volte con gli stessi valori di parametro. Ciò migliora la componibilità perché il risultato di una chiamata di funzione è prevedibile.

  2. Trasparenza referenziale. Un'espressione referenzialmente trasparente valuterà sempre lo stesso risultato. Ciò migliora la componibilità consentendo la sostituzione reciproca di espressioni identiche e la possibilità di calcolare le espressioni in modo indipendente l'una dall'altra (ovvero su thread diversi) senza utilizzare i blocchi.

  3. Immutabilità. Lo stato di un oggetto immutabile non può essere modificato una volta creato. Questo migliora la componibilità perché puoi fare affidamento su un valore stabile dell'oggetto, senza preoccuparti se qualche funzione o oggetto da qualche parte ha cambiato lo stato dell'oggetto dopo che è stato creato.

  4. Purezza. Le funzioni pure non hanno effetti collaterali. Hanno solo un input e un output, il che li rende più compostabili perché puoi inserire l'output di una funzione nell'input di un'altra funzione senza preoccuparti se qualcosa al di fuori della funzione è cambiato.

I blocchi non si compongono perché sono un elemento esterno su cui fare affidamento quando si combinano due operazioni che condividono un certo stato e per tutti i tipi di motivi che hanno a che fare con la complessità inerente all'utilizzo dei blocchi.

La frase "le monadi non compongono" non ha molto senso per me. Il punto centrale di una monade è quello di prendere alcune cose di stato come l'input da tastiera o l'output dello schermo e trasformarlo in una forma matematica più pura che, in effetti, è più compostabile.


4
Penso che stackoverflow.com/questions/7040844/… faccia un buon lavoro spiegando cosa significa probabilmente "monadi non compongono", anche se sono d'accordo che le monadi sono molto più compostabili dei lucchetti.
Ixrec,

I tuoi punti elenco per "trasparenza referenziale" e "purezza" sono in realtà i due requisiti per la purezza . Il termine "trasparenza referenziale" dovrebbe essere evitato perché non è ben definito .
BlueRaja - Danny Pflughoeft,

1
Una monade è una struttura algebrica con determinate operazioni. Quello che hai descritto nel tuo ultimo paragrafo è il IOtipo che cattura "azioni con stato" come l'input da tastiera. I valori del IOtipo, in effetti, compongono, ma è l' intero tipo IO che è una monade. Questo tipo non si compone con altri tipi che sono monadi come, diciamo, il tipo di elenco. Cioè, non possiamo produrre sistematicamente un tipo IO . Listche si comporti come entrambi IO e Listcontemporaneamente. Ha senso? Non sono sicuro di averlo spiegato bene.
Tikhon Jelvis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.