When I pass bytes variable to the method Shell.sh_one
from Core_extended
there is a strange error:
Error: This expression has type bytes but an expression was expected of
type
('a, unit, bytes, bytes option) Core.Std.format4 =
('a, unit, bytes, bytes, bytes, bytes option) format6
What's interesting if I'll pass bytes literal, there is no error. Could someone explain this behaviour of Ocaml? Below there is a listing from Ocaml utop:
# #require "core_extended";;
# open Core_extended.Std;;
# let cmd = "ls -al /";;
val cmd : bytes = "ls -al /"
# "ls -al /";;
- : bytes = "ls -al /"
# Shell.sh_one "ls -al /";;
- : bytes option =
Some
"lrwxrwxrwx 1 root root 30 sty 29 09:28 vmlinuz.old -> boot/vmlinuz-4.13.0-32-generic"
# Shell.sh_one cmd;;
Error: This expression has type bytes but an expression was expected of type
('a, unit, bytes, bytes option) Core.Std.format4 =
('a, unit, bytes, bytes, bytes, bytes option) format6
If you look at the type of Core_extended.Shell.sh_one
, you will see the following
val sh_one: ('a,unit,bytes,string option) format4 -> 'a
This means that the first argument of sh_one
is a format string. For instance, one can use format specifier with sh_one
:
Shell.sh_one "ls -%s /" "al"
Your issue stems from the fact that format string type, format4
, and string or bytes are not the same type in OCaml.
Nevertheless, there is a bit of magic in the OCaml typechecker that makes it possible for strings and format strings to share the same literal syntax: if the typechecker notices that the expected type of string literal is in fact a format string, it reinterpret the string literal as a format string literal.
You can have a look a the phenomenon by yourself in utop by comparing
let s = "A simple string";;
s : string = "A simple string"
and
open CamlinternalFormatBasics
(* ^ this help with making the format readable *)
let fmt : _ format4 = "A format string"
val fmt : ('a, 'b, 'c, 'a) format4 = Format (String_literal ("A simple string", End_of_format), "A simple string")
An alternative to the explicit type annotation is to use the format_of_string
function which marks a string literal as format string literal
let fmt = format_of_string "A format string"
In brief, if you want to store a format string inside a variable you can either use an explicit type annotation or format_of_string
Though they are syntactically identical, the bytes
and format
types are different.
That is handled by some dark magic inside the compiler which basically checks when it sees a string if it is bound to a format type.
In your case, the check is performed at the creation of cmd
. At this point in the program, there is no way to know that it will be used as a format string. So it is given the type bytes
. Later, you get to the usual "I don't do transtyping" from the obviously puzzled compiler.
let cmd : ('a,'b,'c,'d) Core.Std.format4 = "ls -al /";;
Here I just added a type information so that the compiler knows that "this is not a string, but a format string". Things should just work fine with that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.