2012-08-15 10 views
8

Questo modello attivo compila con F # 2.0:modello attivo rotto in F # 3.0

let (|Value|_|) value = // 'a -> 'T option 
    match box value with 
    | :? 'T as x -> Some x 
    | _ -> None 

ma, in F # 3.0, emette l'errore:

Active pattern '|Value|_|' has a result type containing type variables that are not determined by the input. The common cause is a [sic] when a result case is not mentioned, e.g. 'let (|A|B|) (x:int) = A x'. This can be fixed with a type constraint, e.g. 'let (|A|B|) (x:int) : Choice = A x'

ho provato:

let (|Value|_|) value : 'T option = ... 

e:

let (|Value|_|) (value: 'U) = ... 

Come può essere risolto?

Ambienti: Visual Studio 2012 (RTM) e FSI v11.0.50727.1

EDIT: Ecco un Repro semplice:

let (|X|) x = unbox x 
+0

Funziona bene per me, Visual Studio 2012 RC aggiornato, 'Microsoft (R) F # 3.0 build interattiva 11.0.50522.1'. Vedendo l'errore, continuo a pensare che dovrebbe funzionare (così come è). L'esempio nell'errore ('let (| A | B |) (x: int) = A x') presenta effettivamente l'errore che hai postato. –

+1

'F # 2.0 Interactive build 4.0.40219.1' fornisce esattamente gli stessi risultati. –

+0

Scusate, avrei dovuto essere più specifico sull'ambiente. Ho aggiornato la domanda. – Daniel

risposta

4

Si è verificato un errore nel compilatore F # 2.0 in cui il compilatore ha eseguito analisi errate e generazione di codice errato per determinati pattern attivi con variabili di tipo libero nel risultato; una semplice riproduzione è

let (|Check|) (a : int) = a, None 
//let (|Check|) (a : int) = a, (None : int option) 

let check a = 
    match a with 
    | Check (10, None) -> System.Console.WriteLine "10" 
    | Check (20, None) -> System.Console.WriteLine "20" 

check 10 
check 20 

che genera un avviso strano in fase di compilazione e si compila in codice apparentemente errato. Sto indovinando che il nostro tentativo di correggere questo bug (e limitare alcuni casi pazzeschi) in F # 3.0 ha anche rotto alcuni codici legali come danno collaterale della correzione.

Presenterò un altro errore, ma per F # 3.0, sembra che sarà necessario utilizzare uno dei metodi descritti in altre risposte.

+0

È stato corretto per F # 3.1? –

3

non ho installato la nuova versione ancora, ma sono d'accordo questo sembra un po 'pescoso. Immagino che ci possa essere una buona ragione per questa restrizione, ma il tuo esempio nell'altra domanda sembra abbastanza convincente.

Per aggirare il problema, credo che l'aggiunta di un parametro di testimone (che non viene utilizzato, ma accenna quello che il tipo del risultato sarà) potrebbe funzionare:

let (|Value|_|) (witness:unit -> 'T) value : 'T option = 
    match box value with 
    | :? 'T as x -> Some x 
    | _ -> None 

Naturalmente, questo rende il usare un po 'più brutto, perché è necessario venire con qualche argomento. In quanto sopra, ho usato la testimonianza di tipo unit -> 'T, sperando che il seguente potrebbe compilare:

let witness() : 'T = failwith "!" 

match box 1 with 
| Value witness 1 -> printfn "one" 

Se questo non funziona, allora si può probabilmente provare con il parametro testimone di tipo 'T (ma poi si deve fornire un funzione reale, piuttosto che solo una funzione generica).

+0

Grazie per il Non riesco a immaginare una "buona ragione" per rompere qualcosa che funziona in una versione precedente (ed è utile) :-) – Daniel

+0

La soluzione alternativa in [la mia risposta] (http://stackoverflow.com/a/11990910/162396) (ispirato da kvb) a una domanda di follow-up non richiede il parametro 'witness' e conserva l'utilizzo originale. – Daniel

0

Vedere il mio answer nell'altro question per alcune considerazioni su come risolvere il problema e una ragione per cui tali schemi attivi potrebbero non essere desiderabili. Non sono sicuro se il cambiamento di rottura fosse inteso.

2

per ragioni di completezza, un'altra soluzione:

type Box<'R> = Box of obj 

let (|Value|_|) ((Box x) : Box<'R>) : 'R option = 
    match x with 
    | :? 'R as x -> Some x 
    | _ -> None 

let check t = 
    match Box t with 
    | Value 1 -> printfn "one" 
    | Value 2 -> printfn "two" 

check 1 // one 
check 2 // two 

tuttavia ancora soffrirà il problema menzionato da @kvb in another thread. Personalmente preferirò la versione di @kvb con pattern attivo parametrizzato.

Problemi correlati