//[snippet:Implementation] open System open TypeShape open TypeShape_Utils // generates an "ignore" function // for types of shape 'T1 -> 'T2 -> ... -> 'Tn -> unit let rec mkIgnore<'T> () : 'T = cache.GetOrAdd mkIgnoreAux and private mkIgnoreAux<'T> () : 'T = match shapeof<'T> with | Shape.Unit -> unbox () | Shape.FSharpFunc s -> s.Accept { new IFSharpFuncVisitor<'T> with member __.Visit<'Dom,'Cod> () = let ignoreCod = mkIgnore<'Cod>() unbox(fun (_ : 'Dom) -> ignoreCod) } | _ -> failwithf "Type %O is not a unit arrow!" typeof<'T> and private cache : TypeCache = new TypeCache() let ksprintf' (kontOpt : (string -> unit) option) fmt = match kontOpt with | None -> mkIgnore () | Some kont -> Printf.ksprintf kont fmt ksprintf' (Some System.Console.WriteLine) "%s=%d" "key" 42 //[/snippet] //[snippet:Performance] #time "on" // formatting off // Real: 00:00:00.161, CPU: 00:00:00.156, GC gen0: 99, gen1: 0, gen2: 0 for i = 1 to 1000000 do ksprintf' None "Foo=%s, Bar=%d, Baz=%O" "foo" 42 System.ConsoleColor.Red // formatting on // Real: 00:00:01.733, CPU: 00:00:01.750, GC gen0: 316, gen1: 0, gen2: 0 for i = 1 to 1000000 do ksprintf' (Some ignore) "Foo=%s, Bar=%d, Baz=%O" "foo" 42 System.ConsoleColor.Red let value = ([1..5],Some "42") // formatting off // Real: 00:00:00.101, CPU: 00:00:00.109, GC gen0: 80, gen1: 0, gen2: 0 for i = 1 to 1000000 do ksprintf' None "%A" value // formatting on // Real: 00:07:14.970, CPU: 00:07:11.796, GC gen0: 28445, gen1: 43, gen2: 4 for i = 1 to 1000000 do ksprintf' (Some ignore) "%A" value //[/snippet]