2013-08-23 16 views
6

Si consideri il seguente lambda assurdità:F # pattern matching stranezza

function 
| [] -> "Empty list" 
| hd::tl -> "Not so empty list" 

Questo funziona bene. Ora riscrivo come segue:

function 
| [] -> "Empty list" 
| hd::tl & l -> "Not so empty list" 

Anche in questo caso, per motivi senza senso (e so che posso ottenere lo stesso effetto utilizzando as invece di &, ma tutto questo ha a che fare con un problema di codice-golf che è non pertinente a questa domanda). Ora il compilatore F # mi dice:

avviso FS0025: corrispondenze modello incompleta su questa espressione. Nell'esempio , il valore '[]' potrebbe indicare un caso non coperto dai motivi .

Questo non ha alcun senso - Sto gestendo esplicitamente il caso di [] nella prima regola. Non vedo cosa è cambiato dalla prima funzione alla seconda rispetto allo []; la seconda regola di nessuna funzione l'avrebbe uguagliata, ma solo la seconda funzione dà l'avvertimento. Tutto ciò che ho fatto è stato aggiungere un motivo aggiuntivo che corrisponda a , qualsiasi cosa.

Ovviamente, il richiamo della seconda funzione con una lista vuota ha esito positivo.

C'è un motivo valido per cui si è verificato questo avviso o la convalida del modello F # ha semplicemente qualche stranezza? Potrei vedere alcuni casi come questo quando si usano schemi più avanzati, ma questo sembra piuttosto semplice. Anche se il problema non può essere risolto in generale, sembra che questo tipo di caso sia abbastanza comune da meritare una gestione speciale nel compilatore.

+1

non ho mai visto '&' usato in quel modo. Puoi collegare ad alcuni documenti su cosa fa? – JaredPar

+3

@JaredPar: vedere AND pattern qui http://msdn.microsoft.com/en-us/library/dd547125.aspx. Fondamentalmente significa che puoi ottenere la testa, la coda e l'intera lista in un colpo solo. –

risposta

6

Penso che il compilatore F # sia pratico in questo caso.

Alla fine, la seconda regola può essere espresso come un vincolo sulla lista di input xs:

xs = hd :: tl && xs = l 

compilatore F # non sembra per esplorare && vincoli. Questo è ragionevole perché i vincoli possono essere arbitrariamente complessi e l'uso di & è piuttosto raro.

Abbiamo un problema simile con partial active patterns:

let (|Empty|_|) = function 
    | [] -> Some() 
    | _ -> None 

let (|NonEmpty|_|) = function 
    | _ :: _ -> Some() 
    | _ -> None 

// warning FS0025 
let f = function 
    | Empty -> "Empty list" 
    | NonEmpty -> "Not so empty list" 

Per risolvere questo problema, è possibile:

  • Uso as invece di &, che è più appropriato in quanto l è solo un legame.
  • Aggiungere un carattere jolly all'estremità per eliminare l'avviso. Io di solito scrivo

    let f = 
        function 
        | hd::tl & l -> "Not so empty list" 
        | _ -> "Empty list" 
    
  • eliminare l'avviso utilizzando nowarn "25".

+0

Sì, ho già visto l'avviso con i pattern attivi parziali. Questo però ha senso, visto che i pattern attivi sono funzioni compilate e non macro e quindi il compilatore non può verificare staticamente la completezza delle corrispondenze. Ora, se ci fosse un modo per definire un pattern attivo con la parola chiave 'inline' (ho provato), quella potrebbe essere una storia diversa. – luksan

+1

Puoi usare 'inline' con pattern attivi:' lascia inline (| Empty | _ |) xs = match xs con [] -> Some() | _ -> None', ma l'avviso è ancora lì. – pad

+0

Immagino che tu abbia ragione. Anche se faccio '<@@ function [] -> Some() | _ -> Nessuno @@> 'Vedo che AST per l'espressione della corrispondenza è solo un if/else, quindi le informazioni sul pattern sono ancora perse. – luksan

4

immagino che hai trovato un altro caso (a parte when clausole e modelli attivi parziali) in cui procedura di decisione del compilatore non è abbastanza potente. (Ecco perché il messaggio di avviso dice potrebbe indicare :-)).

Se si desidera ottenere la stessa funzionalità senza avvisi, è possibile utilizzare un modello attivo completo come questo (ma in realtà, per gli elenchi, probabilmente andrei con _ per il caso elenco vuoto come suggerisce @pad) :

let (|Empty|NonEmpty|) l = 
    match l with [] -> Empty | x::xs -> NonEmpty(x, xs, l) 

let foo = function 
    | Empty -> 0 
    | NonEmpty(x, xs, l) -> 1 
+0

Si dice "può indicare", ma dice anche "incompleto" piuttosto "eventualmente incompleto", quindi è un po 'ambiguo. – luksan