Penso che tu stia creando una falsa dicotomia qui.
Haskell ha una comprensione monade integrata nella lingua. Una ragione per questo è l'uso delle monadi per l'I / O in stile imperativo. Pertanto, i progettisti di Haskell hanno deciso di farlo apparire come un blocco di codice in un linguaggio generico in stile C, completo di parentesi graffe, punto e virgola e persino return
. I compiti sembrano un po 'non-C, ma la freccia sinistra per l'assegnazione è un tipico pseudo-codice imperativo.
Quindi, le interpretazioni di monade in Haskell sono state progettate per sembrare blocchi di codice sequenziali di effetto collaterale imperativi in un linguaggio C / pseudo-codice generico, perché è un caso d'uso importante per loro. Ciò permea anche la progettazione dell'API con la denominazione della funzione return
sopra menzionata. Nota che il nome di questa funzione non ha molto senso se lo usi al di fuori della notazione do
.
C # ha una comprensione monade integrata nella lingua. Una ragione per questo è l'uso delle monadi per l'interrogazione dei set di dati. Pertanto, i progettisti di C # hanno deciso di farlo sembrare per lo più come SQL o XQuery, completi di FROM
, SELECT
, WHERE
, GROUPBY
, ORDERBY
, ecc. L'ordine sembra un po 'non SQL ( FROM
prima di SELECT
), ma è lo stesso usato da XQuery, in realtà.
Quindi, le interpretazioni di monade in C # sono state progettate per assomigliare a query SQL, perché è un caso d'uso importante per loro. Questo permea anche il design dell'API con la denominazione di ad es. Select
per la funzione di trasformazione (anziché map
), SelectMany
(invece di flatMap
), Where
(invece di select
/ filter
), ecc. Si noti che i nomi di queste operazioni non ha molto senso se li usi al di fuori della comprensione di una query e, in effetti, puoi persino confonderti attivamente ( Select
è ciò che viene solitamente chiamato map
, mentre ciò che è solitamente chiamato select
è Where
).
Scala ha una comprensione monade costruita nella lingua. Una ragione per questo è l'uso delle monadi per le operazioni di raccolta. Pertanto, i progettisti di Scala hanno deciso di farlo sembrare per lo più come un ciclo for
in un linguaggio generico in stile C. I compiti sembrano un po 'non-C, ma la freccia sinistra per l'assegnazione è un tipico pseudo-codice imperativo.
Quindi, le interpretazioni di monade in Scala sono state progettate per apparire come imperative for
loop in un linguaggio C / pseudo-codice generico, perché è un caso d'uso importante per loro.
Si noti che sia nel caso di C # che di Scala, la sintassi di comprensione può effettivamente fare più e / o meno di eseguire solo le due operazioni monadiche join
e bind
(o map
e flatMap
). In Scala, una comprensione for
senza yield
si traduce in foreach
, cioè un'imperativa iterazione con effetti collaterali, che in realtà non ha molto a che fare con le monadi. Un yield
può avere una guardia ( yield foo if bar
), che si traduce in una chiamata a una chiamata a withFilter
, cioè elementi filtranti. C # può fare lo stesso con Where
. C # può anche fare aggregazione ( group by
) e ordinamento ( order by
).
In realtà sono più di una generalizzazione e / o fusione di comprensione di monadi e di liste generalizzate a raccolte e monade arbitrarie. Nota che Haskell ha anche entrambi, ma sono elementi diversi , in C # e Scala, sono fusi insieme.
Haskell ha un sacco di storia e ha aperto la strada a molti concetti. Come è tipico dei pionieri, spesso le persone che vengono dopo di loro, scoprono itinerari migliori, più brevi e più sicuri. Forse se Haskell fosse stato progettato con il senno di poi anziché con l'innovazione, avrebbero anche una comprensione generalizzata delle liste per lavorare con più collezioni, e avrebbero fuso le comprensioni monad e le comprensioni generalizzate insieme, chi lo sa? So che ci sono varianti ed estensioni proposte ad Haskell, che aggiungono ad esempio Arrow Comprehensions.
The words "for" and "yield" seem likely to confuse programmers who are used to, for example, Java's for loop and Ruby's yield.
Il primo è intenzionale. È supposto che assomigli a un for
loop.
Il secondo è sfortunato. La parola yield
ha due significati (in realtà correlati ma non ovviamente così). Uno è il significato usato in concorrenza, coroutine, fibre, discussioni e per la parola chiave yield
e il metodo Fibre#yield
in Ruby, dove significa che un pezzo di codice fornisce il controllo dell'esecuzione su un altro pezzo di codice. L'altro è il significato di un calcolo che produce un risultato. Questo è il significato che viene utilizzato nei generatori Python, nei generatori C # e, interessantemente anche in Ruby, nel metodo Enumerator::Yielder#yield
, ed è l'interpretazione che è intesa per la parola chiave yield
in Scala for
comprehensions.