2011-04-06 4 views
6

Estoy tratando de crear un marco para procesar algunos archivos y datos. La única área con la que estoy luchando es cómo proporcionar una función de registro al marco, lo que permite que el marco informe los mensajes sin tener ningún conocimiento del registro en uso.¿Cómo creo una función F # con un argumento de registro de estilo printf?

let testLogger (source:seq<'a>) logger = 
    logger "Testing..." 
    let length = source |> Seq.length 
    logger "Got a length of %d" length 


let logger format = Printf.kprintf (printfn "%A: %s" System.DateTime.Now) format 
testLogger [1; 2; 3] logger 

Lo ideal sería que yo quiero que este código funcione, pero no puedo encontrar la manera de pasar la función de registro en.

+0

Le puede interesar esto: http://stackoverflow.com/questions/5277902/printf-style-logging-for-f – Daniel

+0

Lo he visto, pero no me ayuda porque no lo hago Quiero que el framework sepa sobre log4net –

+0

Mira la primera respuesta, no tiene nada que ver con log4net. – Daniel

Respuesta

10

Por desgracia, no se puede pasar funciones como printf como parámetros a otras funciones y luego usar ellos con múltiples argumentos diferentes. El problema es que printf es una función genérica del tipo Printf.TextWriterFormat<'a> -> 'a. El tipo real sustituido por el parámetro de tipo 'a es un tipo de función que es diferente cada vez que usa printf (por ejemplo, 'a == string -> unit para "%s", etc.).

En F #, no puede tener el parámetro de una función que a su vez es una función genérica. La función genérica tendrá que ser alguna función global, pero puede parametrizarla por la función que realmente hace algo con la cadena. Esto es esencialmente lo kprintf hace, pero se puede nombrar a su mejor función:

let logPrintf logger format = 
    Printf.kprintf logger format 

Un ejemplo de la función parametrizada por el registrador sería:

let testLogger (source:seq<'a>) logger = 
    logPrintf logger "Testing..." 
    let length = source |> Seq.length 
    logPrintf logger "Got a length of %d" length 


let logger = printfn "%A: %s" System.DateTime.Now 
testLogger [1; 2; 3] logger 
12

Como Tomás señala, funciones en F # puede' t requiere argumentos polimórficos. En este caso, creo que el enfoque de Tomás es bastante bueno, ya que probablemente solo necesites pasar una función string -> unit que se usa para el registro.

Sin embargo, si usted realmente desea pasar alrededor del equivalente de una función polimórfica, una solución es crear un tipo sencillo con un único método genérico, y pasar una instancia de ese tipo:

type ILogger = abstract Log : Printf.StringFormat<'a,unit> -> 'a 

let testLogger (source:seq<'a>) (logger:ILogger) = 
    logger.Log "Testing..." 
    let length = source |> Seq.length   
    logger.Log "Got a length of %d" length 

let logger = { 
    new ILogger with member __.Log format = 
     Printf.kprintf (printfn "%A: %s" System.DateTime.Now) format } 

para hacer este trabajo más bien con la inferencia de tipos, se podría definir un módulo con una función de ayuda sencilla:

module Log = 
    let logWith (logger : ILogger) = logger.Log 

let testLogger2 (source:seq<'a>) logger = 
    Log.logWith logger "Testing..." 
    let length = source |> Seq.length   
    Log.logWith logger "Got a length of %d" length 

este resultado final se parece mucho a la solución de Tomás, pero le da un poco más de flexibilidad en cómo se defina su registrador, que puede o no puede realmente ser útil para ti en este caso.

+0

+1 Me gusta bastante su primer enfoque, ya que puede evitar las funciones globales. –

+0

Buen enfoque. He marcado a Tomás como la respuesta correcta, pero me gusta este enfoque, que probablemente usaré –

Cuestiones relacionadas