tags:

views:

234

answers:

2

I'm a OCaml newbie. I'm playing around with "hello world" type snippets and came across this situation. Here's a session with the interpreter with some extra comments:

# let average a b = 
  (a +. b) /. 2.;;
val average : float -> float -> float = <fun>
# average 1. 4.;;
- : float = 2.5
# string_of_float (average 1. 4.);;
- : string = "2.5"

(* this fails...*)
# let _ = Printf.printf (string_of_float (average 1. 4.));;
Error: This expression has type string but an expression was expected of type
         ('a, out_channel, unit) format =
           ('a, out_channel, unit, unit, unit, unit) format6

(* yet this works *)
# "hello!";;
- : string = "hello!"
# let _ = Printf.printf "hello!";;
hello!- : unit = ()

(* another failed attempt *)
# let s = string_of_float (average 1. 4.);;
val s : string = "2.5"
# s;;
- : string = "2.5"
# let _ = Printf.printf s;;
Error: This expression has type string but an expression was expected of type
         ('a, out_channel, unit) format =
           ('a, out_channel, unit, unit, unit, unit) format6

(* and this also works?? *)
# let _ = Printf.printf "2.5";;
2.5- : unit = ()

So here's the situation. string_of_float (average 1. 4.) returns a string, just as "hello!" does. When I give "hello!" into Printf.printf, it works as expected. When I give string_of_float (average 1. 4.) to Printf.printf it fails and tells me it expected didn't expect a string but that other weird type. But why do "hello!" and "2.5" work then?

What's going on?

+2  A: 
Printf.printf "%s" anyStringExpr

will work. The first argument to printf is somewhat magical. (Others will fill in the details.)

Brian
Yeah, the first argument is a format string containing directives, and when applied returns a function with that number of directives as parameters. Definitely magical.
nlucaroni
Kind of. Anyway it's really great having a fully typechecked printf facility.
bltxd
Hm... this raises more questions. From a functional perspective I would expect that if f(x) = y, g(f(x)) = g(y). Why does this not work with printf? Does it have anything to do with the fact printf will inherently have to have side effects? Is this kind of 'magic' common?
dimatura
@dimatura It has nothing to do with side-effects and everything to do with the fact that the type of `(Printf.printf fmt)` depends of the **contents** of `fmt`. In the case of ordinary functions, the type of `(f x)` depends only of the type of `f` and that of `x`. There is no other hack of such a magnitude in the type system (it's still safe though, just a little strange).
Pascal Cuoq
@Pascal I think I understand a bit better what's happening. It's good to know this behavior isn't widespread.
dimatura
+6  A: 

There is a kind of "overloading" of the meaning of string literals in OCaml. At compile time, they can either be interpreted as a string, or as a format (which are completely different things in the type system), depending on what the type-checker thinks. If it decides that it should be a format, then the format string is parsed directly at compile-time (that is why it is able to type-check the arguments to printf at compile time). (Unlike C, which parses the string at runtime.) However, there is no simple way to convert from a string into a format at runtime. So when you see Printf.printf "2.5", the "2.5" is actually not a string, but as a special format type that was parsed at compile-time. That is why you cannot substitute a string for it.

On an unrelated note, if you just want to print a string, you might want to use print_string (or print_endline if you want a newline).

newacct
That makes sense to me. Thanks!
dimatura