Created
October 22, 2015 21:51
-
-
Save bohdanszymanik/2cee2f5e2dde5fde7c89 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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