tags:

views:

321

answers:

4

All of these start from a simple idea: How to write python-style formatted string in ocaml.

pythoners could init a string as:

str = "this var: %s" % this_var
str2 = "this: %s; that: %s" % (this_var, that_var)

but ocaml's formatted string code as :

let str = Printf.sprintf "this var: %s" this_var
let str2 = Printf.sprintf "this: %s; that: %s" this_var that_var

I believed I can do sth to make the ocaml string formating code python-like At first, I defined a function as below:

let (%) s x = Printf.sprintf s x

then, I can write directly as:

let str = "this: %s" % "sth"

but the simple function cannot handle more complex situations as two or more variables. so I wanted to write a little complex function to make it perfectly simulate the python way. I wrote it as below :

let (%) s li = 
  let split_list = Str.full_split (regexp "%[a-z]") s in
  let rec fmt result_str s_list x_list = match s_list with
    | [] -> result_str
    | shd::stl -> match shd with
       | Text t ->  fmt (result_str^t) stl x_list
       | Delim d -> match x_list with
          | [] -> fmt result_str stl []
          | xhd::xtl -> fmt (result_str^(Printf.sprintf d xhd)) stl xtl
  in 
  fmt "" split_list li

But the function just CANNOT work, because the type error and also ocaml's list cannot contains multiple types. if you write sth like: "name: %s; age: %d" % ["John"; 20] the ocaml compiler world laugh at the code and tell you some type ERROR.

Obviously, I must use Tuple to replace List. but I just do NOT konw how to tail-recursive a variable-length tuple.

any suggestion is welcomed. I have two question indeed.

  1. how to write a pythonic ocaml code to format string.
  2. If Ocaml cannot dynamically generate some string as format6 str and pass it to sprintf? for code:

    let s = "%s" in Printf.sprintf s "hello"

    would generate ERROR info as:

    Error: This expression has type string but an expression was expected of type ('a -> 'b, unit, string) format = ('a -> 'b, unit, string, string, string, string) format6

+4  A: 

(1) I don't think there's a good way to do it that is going to be nicer than using Printf.sprintf directly. I mean, you can extend what you've already come up with:

let (%) = Printf.sprintf
let str3 = ("this: %s; that: %s; other: %s" % this_var) that_var other_var

which works, but is ugly because of the parentheses that are necessary for precedence.

(2) It's really hard to generate a format string at runtime because format strings are parsed at compile time. They may look like string literals, but they are actually a different type, which is "something format6". (it figures out whether you want a string or format string based on the inferred type) In fact, the exact type of a format string depends on what placeholders are in the format; that's the only way that it is able to type-check the number and types of format arguments. It's best not to mess with format strings because they are tied very heavily into the type system.

newacct
+1  A: 

Why would one want to replace statically checked sprintf with some dynamic formatting? OCaml's Printf is both compact in usage and safe at runtime. Compare that to C printf which is compact but unsafe and C++ streams which are safe but verbose. Python's format is no way better than C printf (except that you get exception instead of crashdump).

The only imaginable use-case is format string coming from external source. And it is usually better to move it to compile-time. If it is not possible, then only one needs to fallback to manual dynamic formatting with error-handling (as already said you cannot use Printf with dynamic format string). BTW one such case - internationalization - is covered with existing libraries. Generally, if one wants to dynamically combine multiple values of different types, then one has to wrap them with variants (like ['S "hello"; 'I 20]) and pattern-match at printing side.

ygrek
A: 

If Ocaml cannot dynamically generate some string as format6 str and pass it to sprintf? for code:

let s = "%s" in Printf.sprintf s "hello"

What if we bypass the typing system...

external string_to_format :
 string -> ('a, 'b, 'c, 'd, 'e, 'f) format6 = "%identity"

let s = string_to_format "%s" in (Printf.sprintf s "hello" : string);;

I don't claim this to be the ultimate solution, but that's as far as I got after looking at the mailing list, http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html#printf and ocaml's src.

ruslan
A: 

You should check out OCaml Batteries Included's extended/extensible printf. I have the feeling that you can do what you want with it.

Yoric