2009-08-28 4 views
12

El siguiente código C# funciona como esperaba, la impresión de `s Coincide como 'A':¿Por qué no se pueden parametrizar patrones activos no parciales en F #?

let (|Char|_|) convf = function 
    | LazyList.Nil -> None 
    | LazyList.Cons (x, _) -> Some (convf x) 

let test = function 
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x 
    | _ -> printfn "Didn't match" 

test (LazyList.of_list ['a']) 

Sin embargo, si cambio Char de un patrón activa parcial a un patrón activa completa de la siguiente manera:

let (|Char|NoChar|) convf = function 
    | LazyList.Nil -> NoChar 
    | LazyList.Cons (x, _) -> Char x 

let test = function 
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x 
    | NoChar System.Char.ToUpper -> printfn "Didn't match" 

test (LazyList.of_list ['a']) 

continuación, el código falla al compilar, dando el siguiente mensaje de error: error FS0191: Only active patterns returning exactly one result may accept arguments.

Este ejemplo puede parecer un tanto artificial, pero es una versión simplificada de un patrón activa traté de usar en un Prolo g lexer en que he estado trabajando en mi tiempo libre. Puedo reescribir fácilmente mi código para evitar este problema, pero tengo curiosidad acerca de por qué no se permite este tipo de código.

Actualización: las nuevas versiones de F # parecen haber cambiado el nombre de este error:

error FS0722: Only active patterns returning exactly one result may accept arguments

Respuesta

13

NB. Esto es exactamente lo que dijo Brian, pero espero que se establezca de una manera más clara.

Recuerdo registrar un error precisamente en este tema y IIRC esto es lo que Don Syme tuvo que decir al respecto.

Un patrón activo de varias cajas es una función de conversión de un valor de entrada a uno de varios valores de salida. En su ejemplo, cualquier personaje se convierte en el caso Char o en el caso NoChar.

El beneficio de esto es que el compilador de F # llama a la función de patrón de varias casillas una vez y, en general, puede determinar qué regla de coincidencia de patrón evaluar a continuación.

Sin embargo, si permite un parámetro, debe evaluar el patrón de varias casillas activas para cada regla de coincidencia de patrón.

así que imagina la siguiente

match input with 
| Alpha "foo" -> ... 
| Bravo "bar" -> ... 

Al evaluar (| Alpha | Bravo |) "foo" devolvió 'Bravo', entonces la primera regla no va a coincidir. Likeways (| Alpha | Bravo |) "barra" devuelve "Alpha", entonces la segunda regla tampoco coincide. Entonces realmente no tienes un patrón de múltiples casos activos. Solo un patrón activo parcial y paramétrico.(Debido a que para algunas entradas el patrón de casos esperado no será golpeado.)

Así que cuando se enfrentan con un rincón del lenguaje que no tiene mucho sentido, y de hecho puede hacerse mucho más claro al el patrón activo parcial parametrizado. La función no se agregó al idioma.

4

No puedo decir con certeza (no sé justificación diseño real), pero tratando de revertir -ingeniero, ¿qué esperarías que hiciera este código?

let (|Char|NoChar|) pred = function  
    | LazyList.Nil -> NoChar  
    | LazyList.Cons (x, _) -> if pred x then Char x else NoChar 
let test = function  
    | Char System.Char.IsLetter x -> printfn "Matched as %A" x  
    | NoChar System.Char.IsDigit -> printfn "Didn't match" 
test (LazyList.of_list ['a']) 
test (LazyList.of_list ['1']) 

Teniendo en cuenta que los patrones activas y no parciales se supone que la partición de todo el espacio, sería raro si usted le dio a cada uno un argumento diferente dentro del mismo partido, porque entonces puede ser que 'tanto fallar' o 'tanto éxito '. (También sugiere cómo pueden implementarse, por ejemplo, patrones que capturan su argumento antes de hacer la coincidencia. El argumento capturado sería invariable en todas las ramas del partido.)

También sugiere que pueda escribir, p.

let test convf l = 
    let (|Char|NoChar|) = function  
     | LazyList.Nil -> NoChar  
     | LazyList.Cons (x, _) -> Char(convf x) 
    match l with 
    | Char x -> printfn "Matched as %A" x  
    | NoChar -> printfn "Didn't match" 
test System.Char.ToUpper (LazyList.of_list ['a']) 

(aunque no sé si esto es conveniente/realista para su aplicación en particular).

Cuestiones relacionadas