2010-06-26 20 views
30

Sto provando a verificare qualcosa sull'operatore e sulla precedenza delle funzioni in Haskell. Per esempio, il codice seguenteHaskell operatore vs funzione precedenza

list = map foo $ xs 

può essere riscritta come

list = (map foo) $ (xs) 

e alla fine sarà

list = map foo xs 

La mia domanda di una volta, perché la prima formulazione non sarebbe essere riscritta come

list = (map foo $) xs 

poiché la precedenza della funzione è sempre superiore alla precedenza dell'operatore, ma penso di aver trovato la risposta: gli operatori non possono semplicemente essere argomenti di funzioni (eccetto ovviamente, se li circondano con parentesi). È giusto? Se è così, trovo strano che non ci sia menzione di questa meccanica/regola in RWH o Learn you a Haskell, o in nessuno degli altri posti che ho cercato. Quindi, se conosci un luogo, dove viene dichiarata la regola, ti preghiamo di collegarti ad esso.

- modifica: Grazie per le risposte rapide. Penso che la mia confusione sia venuta dal pensare che un operatore letterale avrebbe in qualche modo valutato qualcosa, che potrebbe essere consumato da una funzione come argomento. Mi ha aiutato a ricordare che un operatore infisso può essere tradotto meccanicamente in funzioni prefissate. In questo modo le prime rese formulazione

($) (map foo) (xs) 

in cui v'è alcun dubbio che ($) è la funzione che consumano, e dal momento che le due formulazioni sono equivalenti, quindi il $ letterale nella prima formulazione non può essere consumato da mappa.

risposta

20

Sei corretto. Questa regola fa parte della sintassi Haskell definita da Haskell Report. In particolare, si noti nella Sezione 3, Espressioni, che l'argomento per l'applicazione della funzione (uno fexp) deve essere un aexp. Un aexp consente agli operatori come parte di sezioni e anche all'interno di un'espressione tra parentesi, ma non di operatori nudi.

In map foo $ xs, la sintassi Haskell indica che questo viene analizzato come due espressioni che vengono applicate all'operatore binario $. Come osserva sepp2k, la sintassi (map foo $) è una sezione sinistra e ha un significato diverso.

Devo confessare che non ho mai pensato molto a questo e in realtà ho dovuto cercare nel rapporto per vedere perché gli operatori hanno il comportamento che fanno.

8

Gli operatori possono essere passati come argomenti di funzione se li si circonda con parentesi (ad esempio map foo ($) xs, che sarebbe effettivamente passato come (map foo ($)) xs). Tuttavia, se non li circondi con parentesi, hai ragione che non possono essere passati come argomento (o assegnati a variabili).

noti inoltre che la sintassi (someValue $) (dove $ potrebbe essere qualsiasi operatore) significa in realtà qualcosa di diverso: è equivalente a \x -> someValue $ x, cioè vale parzialmente l'operatore alla operando sinistro (che nel caso di $ è un noop naturalmente). Analogamente, ($ x) applica parzialmente l'operatore all'operando di destra. Quindi map ($ x) [f, g, h] valuterebbe a [f x, g x, h x].

27

In primo luogo, l'applicazione (spazio bianco) è il "gestore" con la precedenza più alta.

In secondo luogo, in Haskell, non c'è davvero alcuna distinzione tra operatori e funzioni, a parte il fatto che gli operatori sono predefiniti per impostazione predefinita, mentre le funzioni no. È possibile convertire le funzioni di infisso con apici inversi

2 `f` x 

e convertire operatori di prefisso con parentesi:

(+) 2 3 

Quindi, la sua domanda è un po 'confuso.

Ora, funzioni e operatori specifici avranno dichiarato la precedenza, che si può trovare in GHCi con ": info":

Prelude> :info ($) 
($) :: (a -> b) -> a -> b -- Defined in GHC.Base 

infixr 0 $ 

class (Eq a, Show a) => Num a where 
    (+) :: a -> a -> a 

infixl 6 + 

mostra sia la precedenza e l'associatività.

+0

Grazie. Dopo aver letto la tua risposta, quando dici che la mia domanda è un po 'confusa, prendo per capire che la mia menzione di "precedenza della funzione" è sbagliata, e che le funzioni in se stesse non hanno alcuna precedenza, ma piuttosto possono essere pensate come argomenti per l'operatore dell'applicazione, spazi bianchi. È corretto? Non sono ancora del tutto chiaro su come gli operatori di infix si adattano a questo. Ho modificato la mia domanda per riflettere su come posso pensarci meglio. Questa comprensione è corretta? – Boris

+0

No, non proprio. Funzioni e operatori sono indistinguibili in Haskell. Possono entrambi avere diversi livelli di precedenza, specificati dall'utente. –

+3

Ok. Sto ancora cercando di capire la regola generale, che porta a list = (map pippo) $ (xs) invece di list = (map pippo $) xs - che so essere una sezione. Si può dire che quando si decide, per una determinata espressione, quali argomenti appartengono a quali funzioni e operatori, le funzioni consumano - a partire da sinistra - tutti gli argomenti che possono, finché non raggiungono un operatore.Dopo questo, gli operatori utilizzano i loro argomenti, in base alla loro precedenza e associatività (questo è un po 'vago, ma spero che tu capisca). Scusa, se questo sembra ovvio, ma non è mai stato così chiaro per me. – Boris

10

La differenza è che operatori infissi vengono posizionati tra loro argomenti, quindi questo

list = map foo $ xs 

possono essere riscritte in forma prefisso

list = ($) (map foo) xs 

che, per la definizione di operatore $, è semplicemente

list = (map foo) xs 
9

In aggiunta alle informazioni fornite da altre risposte già, si noti che operatori diversi possono avere precedenti diversi rispetto ad altri operatori, nonché essere di sinistra/destra o non associativa. È possibile trovare queste proprietà per gli operatori Prelude nello Haskell 98 Report fixity section.

 
+--------+----------------------+-----------------------+-------------------+ 
| Prec- | Left associative | Non-associative | Right associative | 
| edence |  operators  |  operators  | operators  | 
+--------+----------------------+-----------------------+-------------------+ 
| 9  | !!     |      | .     | 
| 8  |      |      | ^, ^^, **   | 
| 7  | *, /, `div`,   |      |     | 
|  | `mod`, `rem`, `quot` |      |     | 
| 6  | +, -     |      |     | 
| 5  |      |      | :, ++    | 
| 4  |      | ==, /=, <, <=, >, >=, |     | 
|  |      | `elem`, `notElem`  |     | 
| 3  |      |      | &&    | 
| 2  |      |      | ||    | 
| 1  | >>, >>=    |      |     | 
| 0  |      |      | $, $!, `seq`  | 
+--------+----------------------+-----------------------+-------------------+ 

Qualsiasi operatore privo dichiarazione fixity si presume essere associativo fianco con precedenza 9.

Ricordate, l'applicazione ha la funzione precedenza più alta (si pensi di precedenza 10 rispetto alle altre precedenze nella tabella) [1].

+0

Come mai 'f g. h' è equivalente a 'f (g.h)'? Questo fa sì che la precedenza dell'applicazione della funzione sia inferiore o uguale alla precedenza della composizione della funzione? – CMCDragonkai

+0

@CMCDragonkai Ho aggiornato la risposta con la precedenza dell'applicazione della funzione. 'f g. h' è equivalente a '(f g). h' come applicazione di funzione ha precedenza più alta di qualsiasi precedente operatore (inclusa la composizione delle funzioni). – mucaho