Skip to content

Instantly share code, notes, and snippets.

@bohdanszymanik
Created October 22, 2015 21:51
Show Gist options
  • Save bohdanszymanik/2cee2f5e2dde5fde7c89 to your computer and use it in GitHub Desktop.
Save bohdanszymanik/2cee2f5e2dde5fde7c89 to your computer and use it in GitHub Desktop.
// getting to grips with PrintfFormat
//let sscanf (pf:PrintfFormat<_,_,_,_,'t>) s : 't =
let explorePF (pf:PrintfFormat<'printer,'state,'residue,'result,'tuple>) s : string =
pf.Value
explorePF "(%s %% % %i)" "(somestring)"
explorePF "(%s %% %z %i)" "(somestring)" // error FS0741: Unable to parse format string 'Bad format specifier: 'z''
let explorePFGeneric (pf:PrintfFormat<'printer,'state,'residue,'result,'tuple>) s : 't =
pf.Value // has a little moan about 't being constrained to be less generic since .Value returns a string
explorePFGeneric "(%s %% % %i)" "(somestring)"
(* from page 75 of the F# spec 3.1
More concretely, a constant string is interpreted as a printf-style format string if it is expected to have the type Microsoft.FSharp.Core.PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>. The string is statically analyzed to resolve the generic parameters of the PrintfFormat type, of which 'Printer and 'Tuple are the most interesting:
• 'Printer is the function type that is generated by applying a printf-like function to the format string.
• 'Tuple is the type of the tuple of values that are generated by treating the string as a generator (for example, when the format string is used with a function similar to scanf in other languages).
A format placeholder has the following shape:
%[flags][width][.precision][type]
*)
// set 'printer?
// let's say we expect 'printer to deliver a string - entirely reasonable
let ``explorePF with string to string`` (pf:PrintfFormat<(string -> string),'state,'residue,'result,'tuple>) s : string =
pf.Value
``explorePF with string to string`` "((%s %s))" "((1 string))"
(* Type mismatch. Expecting a
string -> string
but given a
string -> string -> 'a
The type 'string' does not match the type 'string -> 'a'
*)
``explorePF with string to string`` "((%s))" "((1 string))" // works nice
let ``explorePF with int and string to string`` (pf:PrintfFormat<(int -> string -> string),'state,'residue,'result,'tuple>) s : string =
pf.Value
``explorePF with int and string to string`` "--%i %s some other stuff" "123 and"
``explorePF with int and string to string`` "--%i %s some other stuff" "123 123"
``explorePF with int and string to string`` "--%i %s some other stuff" ""
``explorePF with int and string to string`` "--%i %s some other stuff" //gives an error
(*
Value restriction. The value 'it' has been inferred to have generic type
val it : ('_a -> string)
Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.
*)
// so with the 'Printer we can enforce some type checking on the format parameters but so far we're not getting access to the actual string
(*
Also, note example from spec on page 76
"For example, the format string "%s %d %s" is given the
type PrintfFormat<(string -> int -> string -> 'd), 'b, 'c, 'd,(string * int * string)> for fresh variable types 'b, 'c, 'd. Applying
printf to it yields a function of type string -> int -> string -> unit."
*)
let ``explorePF with int and string to string and tuple`` (pf:PrintfFormat<(int -> string -> string),'state,'residue,'result,(int * string)>) s : string =
pf.Value
``explorePF with int and string to string and tuple`` "--%i %s some other stuff" "123 and"
``explorePF with int and string to string and tuple`` "--%i %s some other stuff" "123 and 123"
``explorePF with int and string to string and tuple`` "%i %s %s some other stuff" "123 and 123"
// let's simplify
fun _ -> "%i %s":PrintfFormat<(int -> string -> string),'state,'residue,'result,'tuple>
fun _ -> "%s %s":PrintfFormat<(int -> string -> string),'state,'residue,'result,'tuple> // failed type test on format string - good
fun _ -> "%s %s":PrintfFormat<'printer,'state,'residue,'result,(int * string)> // failed type test on 'tuple string - good
fun _ -> "%i %s":PrintfFormat<'printer,'state,'residue,'result,(int * string)> // passed - good
// nice, so can see how we can enforce type restriction on the 'printer and on the 'tuple
// so can we apply the PrintfFormat type to a printf statement (actually function)?
printfn ("%i %s":PrintfFormat<'printer,'state,'residue,'result,(int * string)>) 123 "test"
printfn ("%i %s":PrintfFormat<'printer,'state,'residue,'result,(string * string)>) 123 "test" // nice, fails type restriction on 'tuple
// can we check for correctness of the 'result?
printfn ("%i %s":PrintfFormat<'printer,'state,'residue,unit,(int * string)>) 123 "test" // as expected
printfn ("%i %s":PrintfFormat<'printer,'state,'residue,string,(int * string)>) 123 "test" // nice, fails as expected
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment