简体   繁体   中英

How to pass parameter to Shell.sh_one in Ocaml

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM