Qual è la logica dietro l'uso di diverse frecce (- -) in Haskell?

5

Ultimamente ho pensato al design delle lingue e ho letto alcune delle novità di Haskell (sempre una buona fonte di ispirazione). Sono colpito dai molti usi strani degli operatori freccia sinistra <- e destra -> .

Credo che i molti usi diversi provengano dalla tecnica precedente in matematica e altri linguaggi relativi alla sintassi delle frecce, ma c'è qualche altra ragione per non provare a rendere l'uso più coerente o chiaro? Forse non vedo il quadro generale?

La freccia destra viene usata come un costruttore di tipi per le funzioni, il separatore tra argomento e corpo di espressioni lambda, separatore per istruzioni case, ed è usato nelle viste pattern, che hanno la forma (e -> p) .

La freccia sinistra viene usata in do notation come qualcosa di simile al binding di variabili, in list comprehensions per lo stesso (presumo che siano uguali, come le list comprehensions sembrano condensate do blocks), e in guardie del modello, che hanno la forma (p <- e) .

Ora gli ultimi esempi per ogni freccia sono semplicemente sciocchi! Capisco che guardie e punti di vista hanno scopi diversi, ma hanno una forma quasi identica, tranne che uno è lo specchio dell'altro! Ho anche sempre trovato strano che le funzioni regolari siano definite con = ma lambdas con -> . Perché non usare la freccia per entrambi? O gli uguali per entrambi?

Diventa anche piuttosto strano se consideri che per confrontare un valore calcolato con una costante, ci sono quasi una mezza dozzina di modi per farlo:

test x = x == 4

f     x = if test x then g x else h x

f'    4 = g 4
f'    x = h x

f''   x@(test -> true) = g x
f''   _ = h x

f'''  x | true <- test x = g x
        | otherwise = h x

f'''' x = case (x) of 
          4 -> g x
          _ -> h x

La varietà è lo spezia del codice sorgente, giusto?

    
posta CodexArcanum 28.08.2014 - 23:55
fonte

2 risposte

5

I've also always found it kind of odd that regular functions are defined with = but lambdas with ->. Why not use the arrow for both? Or the equals for both?

Il simbolo di uguaglianza ha senso per le definizioni di funzioni nominate perché afferma che l'applicazione della funzione denominata ai suoi argomenti equivale a valutare il lato destro dell'equazione. La definizione di lambda in questo modo non ha senso notazionalmente: x = x + 1 implica x è uguale al suo successore, che non può essere vero.

The left arrow gets used in do notation as something similar to variable binding, in list comprehensions for the same (I'm assuming they are the same, as list comprehensions look like condensed do blocks), and in pattern guards, which have the form (p <- e).

Tutti questi costrutti associano una nuova variabile. L'utilizzo di = qui non avrebbe molto senso, perché un'istruzione come x = x + 1 implica che x si riferisca alla stessa cosa su entrambi i lati. Un binding variabile introduce un nuovo x che è diverso da qualsiasi x che può verificarsi nel lato destro.

Un'alternativa sarebbe stata utilizzare solo -> e posizionare la variabile a destra, ma credo che collocarla a sinistra sia oggettivamente più leggibile nelle culture che vanno da sinistra a destra. Scansione del testo spostando gli occhi lungo il margine sinistro e posizionando quindi tutte le variabili a sinistra rende sono più facili da trovare. E anche se avere due simboli diversi per i collegamenti variabili può non sembrare coerente, c'è coerenza nella posizione della variabile che viene vincolata; è sempre a sinistra.

In questo modo viene lasciata la domanda sul perché <- rispetto ad altri simboli come := . Non lo so, ma posso indovinare. Guarda come appare il codice monadico desugared:

[1, 2, 3] >>= \xs -> map (*2) xs

Considerando che >>= con i suoi argomenti scambiati si chiama =<< , probabilmente sembrava naturale utilizzare una freccia rivolta a sinistra.

    
risposta data 11.03.2015 - 15:14
fonte
1

L'operatore <- viene utilizzato in due modi, come descritto in Haskell operatori :

  1. Generatore di comprensione degli elenchi;
  2. Operatore di assegnazione singola in do-constr.

Le list comprehensions sono zucchero sintattico come l'espressione

import Data.Char (toUpper)

[toUpper c | c <- s]

dove s :: String è una stringa come "Hello". Le stringhe in Haskell sono liste di caratteri; il generatore c <- s alimenta ogni carattere di s a sua volta all'espressione di sinistra toUpper c , costruendo un nuovo elenco. Il risultato di questa comprensione della lista è "CIAO". (Naturalmente, in questo semplice esempio si scriverebbe solo map toUpper s .)

Nelle prime versioni di Haskell, la sintassi di comprensione era disponibile per tutte le monadi. Più tardi la sintassi di comprensione era limitata alle liste. Poiché le liste sono un'istanza di monadi, è possibile ottenere la comprensione delle liste in termini di fare notazione.

Per questo motivo, diversi programmatori Haskell considerano la comprensione della lista non necessaria ora.

L'esempio di sopra può essere tradotto in lista monad come segue:

do c <- s return (toUpper c)

Per maggiori informazioni, vedi Comprensione delle liste .

    
risposta data 11.03.2015 - 12:50
fonte

Leggi altre domande sui tag