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.)
Related
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.
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.
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.
I sometimes write functions that make the assumption that some arguments cannot occur. If they do, this is a bug and I fail:
let foo = function
| 0 -> ()
| _ -> failwith "foo: bad argument"
If I rename the function later on, I have to remember to also change the string. Is there a way to do this in a more systematic manner? My mind wanders around solutions like
| _ -> failwith (FUNCTION_NAME ^ ": bad argument")
where FUNCTION_NAME is a string variable that the compiler or interpreter will instantiate. But I have no idea whether something like this even works in OCaml. If not, is there a best practice?
There is a set of values available for debugging and error reporting.
OCaml 4.12 introduced __FUNCTION__:
val __FUNCTION__ : string
__FUNCTION__ returns the name of the current function or method, including any enclosing modules or classes.
They might be useful if you don't want to use assert false as suggested by #SteveVinoski.
__LOC__ : string
__FILE__ : string
__LINE__ : int
__MODULE__ : string
__POS__ : string * int * int * int
There are also fancier forms that you can use to wrap an expression to determine its extent in the source.
These are documented in the Stdlib module.
I think you should try as much as possible to use more specific types so that the compiler can prevent you from calling your functions with invalid input.
If you really don't find a way to tell the type system what you want, try harder, and if you really can't, then use assert which as Steve told you will give you some precious debugging information (file and line number are much more useful than function name since chances are high your function doesn't have a name).
Since 4.12.0, __FUNCTION__ will return the function name.
There are also a number of other debugging variables defined in Stdlib:
val __LOC__ : string
val __FILE__ : string
val __LINE__ : int
val __MODULE__ : string
val __POS__ : string * int * int * int
val __FUNCTION__ : string
val __LOC_OF__ : 'a -> string * 'a
val __LINE_OF__ : 'a -> int * 'a
val __POS_OF__ : 'a -> (string * int * int * int) * 'a
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 ;;