EDIT: I'm sorry everyone, I thought my small examle was complete, turns out it's not. I made a new one that really should be!
As soon as I use a formatter as parameter to Scanf or Printf functions the formatter type gets bound to a in- or output-channel respectively. Is there a way to have a function take a formatter (or string) and use that as a formatter for both printing and reading?
let fmt = format_of_string "%d,%d";;
Scanf.sscanf "2,2" fmt (fun x y -> x,y);;
fmt;;
gives
- : (int -> int -> int * int, Scanf.Scanning.scanbuf, '_a, (int -> int -> int * int) -> int * int, (int -> int -> int * int) -> int * int, int * int) format6 = <abstr>
Which means a subsequent Printf.printf fmt 1 2;;
gives a type error.
This goes for every combination of format_of_string
and Scanf.format_from_string
like functions I've tried.
Example:
module Thing = struct
(* Just a helper for file IO *)
type 'a result = Success of 'a | Failure of exn;;
let with_out_file filename fn =
let out_ch = open_out filename in
let res = try Success (fn out_ch) with
exn -> Failure exn in
close_out out_ch;
match res with
| Success a -> a
| Failure a -> raise a;;
(* Uses the format string for writing *)
let print (fmt : ('a, 'b, 'c, 'd, 'e, 'f) format6) fn v =
with_out_file fn (fun x -> Printf.fprintf x fmt v);;
(* Uses the format string for reading *)
let read (fmt : ('a, 'b, 'c, 'd, 'e, 'f) format6) v =
Scanf.sscanf v fmt (fun x -> x);;
(* Where things break *)
let both fmt v =
read fmt "42\n";
print fmt "tfile" v;;
end;;
Gives
Error: This expression has type ('a -> 'b, Scanf.Scanning.scanbuf, 'c, ('d -> 'd) -> 'e, ('a -> 'b) -> 'f, 'f) format6 but an expression was expected of type
('a -> 'b, out_channel, unit, unit, unit, unit) format6
Type Scanf.Scanning.scanbuf is not compatible with type out_channel
For the last line of the both
function, which seems to make sense,
but if I remove the both
function from the module, I can call read
and print
with the same format string (same variable as parameter) and it just works.
So, with hopes that you guys haven't given up on me yet; how do I get around that? neither eta-expansion nor type annotation seems to work in this case?
You're hitting the infamous value restriction:
Those
'_a
,'_b
,'_c
,'_d
mean "types to be determined as soon as we can". They are not parameters, i.e.,fmt
is not a polymorphic value. In contrast, an empty list is polymorphic:Now we have
'a
and not'_a
. But maybe you already know all this. The point is that when we applyPrintf.printf
tofmt
, its type gets fixed asbut when we apply
Scanf.sscanf
tofmt
its type gets fixed asThese two types are not compatible, and because
fmt
is not polymorphic, you cannot use it both ways. The solution is simply to have two copies,fmt_in
andfmt_out
. Is there something unacceptable with that solution?