OCaml function format_of_string is not working as expected - ocaml

I want to write a function that accepts a pair of strings and convert the second element into a format. I thought I could use the format_of_string function, but it seems that is doesn't work. This is a simplified version:
let pp fmt (e:string * string) =
let msg = format_of_string (snd e) in Format.fprintf fmt (" - "^^msg^^"#.");;
^^^^^^^
Error: This expression has type string but an expression was expected of type
('a, 'b, 'c, 'd, 'e, 'f) format6
Is there a way to make my function accept a pair of strings ?

Counterintuitively, format_of_string does not convert a string into a format (that is not possible to do in a way that is guaranteed to work, because format types are type-checked at compile time, but the content of the string is not known at compile time; Scanf.format_from_string checks it at runtime).
Rather, its type is val format_of_string : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> ('a, 'b, 'c, 'd, 'e, 'f) format6 -- it takes a format and returns a format, which means it is essentially an identity function on formats.
What's the use of such a function? Unusually for expressions in OCaml, string literals in OCaml are "overloaded". They can be either a string type or a format type. Most of the time, it's inferred as a string type. But if you use it in a context that expects a format type, e.g. an argument to Printf.printf, the compiler infers it as a format type. But what about if you use it to initialize a variable? The compiler "defaults" to a string type. However, there may be cases when you want to initialize a variable of format type. You can't easily use a type annotation, because formats have really complicated types and you don't want to have to specify it explicitly. Instead, you can pass the string literal through format_of_string, which "forces" the compiler to infer it as a format type, but otherwise returns the value unchanged.
P.S. would it be possible for you to take a format instead of a string in the parameter?

I answer my own question but I found the solution thanks to #Pascal Cuoq in the comments above.
The problem is that the compiler is able to convert a string into a format only if it is able to analyze it in order to compute the format type. That is why format_of_string is only working on known strings. So a first solution of the problem above would be to convert the string before calling the function, when it is known, but it is not a real answer to the question.
The best solution is to use Scanf.format_from_string:
val format_from_string : string ->
('a, 'b, 'c, 'd, 'e, 'f) format6 -> ('a, 'b, 'c, 'd, 'e, 'f) format6
where is second argument has the same type than the intended format. So for instance, in the example above, because the string souldn't include any % argument, it would be :
let pp fmt (e:string * string) =
let msg = Scanf.format_from_string (snd e) "" in
Format.fprintf fmt (" - "^^msg^^"#.");;
Then calling this function is correct only which a string with no % argument:
# Format.printf "%a" pp ("", "abc");;
- abc
- : unit = ()
But it raises an exception otherwise:
# Format.printf "%a" pp ("", "abc%d");;
Exception:
Scanf.Scan_failure "format read 'abc%d' does not match specification ''".

You cannot take a format as argument and catenate it with other bits of strings and use that as an format: the type system does not allow to express this.
Just think of the case where " - " would have been "%". It is a different example, but it has the same type, so it must be accepted or rejected with the same mechanisms.
Does the line below do what you originally intended?
let pp fmt f x = Format.printf fmt " - %a#." f x ;;

Related

Ocaml Conversion String to int to char

I need to implement this function somewhere
String.get: string -> int -> char
I have tried this one but it does not seem to work
let String.get = fun x -> char_of_int(int_of_string x) ;;
The error I get is:
let String.get = fun x -> char_of_int(int_of_string x) ;;
^^^
Error: Syntax error
String.get is a syntax to denote the function get in module String. The syntax can not be used to (re)define a function as you wrote.
The function is documented here:
val get : string -> int -> char
String.get s n returns the character at index n in string s. You can also write s.[n] instead of String.get s n.
Raise Invalid_argument if n not a valid index in s.
What you are trying to implement is different, you are trying to read, from the string, an integer, and then convert it to a digit char (?)
Depending on what your actual requirements are, you might be asked to reimplement String.get on your own, so for example you would pick a different name in your current module (for now, this is sufficient, you don't need to bother about modules):
let char_at s n = ...
Or maybe you do actually need to convert from an integer. Please clarify your question.

Printing user defined types in Ocaml

I am defining a new type which is basically a string. How to print the value ?
# type mytp = Mytp of string;;
type mytp = Mytp of string
# let x = Mytp "Hello Ocaml";;
val x : mytp = Mytp "Hello Ocaml"
# print_endline x;;
Error: This expression has type mytp but an expression was expected of type
string
#
This question already has answer here.
There is another question similar to this, which I had went through before asking the question, however I was not clear (maybe because I am a complete newbie. Other newbies might face similar confusion.) how to solve the problem from the accepted answer.
The type of print_endline is string -> unit. So you can't pass a value of type mytp.
You can write a function to print a value of type mytp:
let print_mytp (Mytp s) = print_endline s
You can write a function to convert mytp to string:
let string_of_mytp (Mytp s) = s
Then you can print like so:
print_endline (string_of_mytp x)
OCaml will not allow you to use mytp where string is expected, or vice versa. This is a feature, not a bug.

expression expected type unit, but it already does

let _ =
try ("hello"; ()) with
| _ -> print_endline "hi"
Compiling this tells me that ("hello"; ()) 'should have type unit'
In fact, I get the same warning with this code
let _ = "hello"; ()
or this code
let _ = ("hello"; ())
But it does have type unit ... doesn't it?
The expression :
let f = "hello";1;;
Triggers the warning :
this expression should have type unit - around "hello" string.
This is because you are trying to return a first value via "hello", and then you return 1 meaning that ocaml must disregard "hello".
if you replace it by unit - meaning "here I return nothing", it will be ok.
The expression :
let f = (); 1;;
raises no warning and f is an int.
So the warning you are getting is related to the inner code of your expression, not to the type of the expression you have written.
let f = "hello";();;
The compiler warns you that you compute something that you disregard after that ("hello" is never used, and the return value is of f is ()). But, as you have noted, f has type unit.
In utop :
let _ = try ("hello"; ()) with
| _ -> print_endline "hi";;
You get :
Characters 13-20:
Warning 10: this expression should have type unit.
which locates exactly to the position of "hello" string - but does not locate to ("hello"; ()). ("hello"; ()) has type unit, exactly like print_endline "hi".
The warning is just about the fact that the expression that should be instead of "hello"; is expected to have type unit.

Converting OCaml strings to format6

The following code does not compile:
let x = "hello" in
Printf.printf x
The error is:
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
1) Can someone give an explanation of the error message?
2) And why would a string cannot be passed to printf ?
The first argument to printf must be of type ('a, out_channel, unit) format not string. String literals can be automatically converted to an appropriate format type, but strings in general can't.
The reason for that is that the exact type of a format string depends on the contents of the string. For example the type of the expression printf "%d-%d" should be int -> int -> () while the type of printf "%s" should be string -> (). Clearly such type checking is impossible when the format string is not known at compile time.
In your case you can just do printf "%s" x.
As sepp2k points out, in OCaml printf formats have a distinct type, and are not simply strings. String literals are converted automatically to printf formats, but x is not a string literal. If you want to give a name to a format, you can convert it explicitly yourself:
> let x = format_of_string "hello" in Printf.printf x
hello- : unit = ()
You can also cause the implicit conversion by specifying a type for x, but the types of formats are so complicated this is quite painful:
# let (x: ('a,'b,'c,'d,'d,'a) format6) = "hello" in Printf.printf x;;
hello- : unit = ()
(I personally don't understand the format6 type.)

OCaml: get value's type name

Is is possible to print value's name in OCaml, for example if I have
type my_type =
| MyType_First of int
| MyType_Second of string
and then do something like:
let my_value = MyType_First 0 in
print_string ("my_value is of type " ^ String.from_type my_value ^ ".\n";
can I get "my_value is of type MyType_First." ?
Thank you.
Monomorphic solution:
let from_type = function
| MyType_First _ -> "MyType_First"
| MyType_Second _ -> "MyType_Second"
Polymorphic solution: none. (AFAIK, lexical tokens corresponding to constructors are not recorded in the bytecode/binary, even when debugging flags are specified. The only thing one could do is to print the integer ‘identifier’ for the constructor, using some dark Obj.magic.)
What you want is a simpler form of generic print and is not available in OCaml as such, but some workarounds exist - e.g. deriving.