I am trying to define a function msg which will allow printing debug messages using Format library with an option to control if they displayed or not via debug flag (set from a command line). Here is my naive first attempt:
let debug = ref false
let msg =
let open Format in
(if !debug then fprintf err_formatter else ifprintf err_formatter)
Unfortunately, this does not work, giving me the following error message:
Error: The type of this expression,
('_a, Format.formatter, unit) format -> '_a,
contains type variables that cannot be generalized
What am I doing wrong? Thanks!
This is the value restriction. You can solve it by "eta expansion":
let msg x =
let open Format in
if !debug then fprintf err_formatter x
else ifprintf err_formatter x
Briefly stated, the value restriction avoids unsound behavior by prohibiting the generalization (treatment as fully polymorphic) of anything other than simple values. Your definition for msg is not a simple value. After eta expansion, the definition is a simple value (a lambda). You can read more here: Jacques Garrigue, Relaxing the Value Restriction.
The new code works for me:
# let debug = ref true
val debug : bool ref = {contents = true}
# let msg x =
let open Format in
if !debug then fprintf err_formatter x
else ifprintf err_formatter x;;
val msg : ('a, Format.formatter, unit) format -> 'a = <fun>
# let open Format in
msg "%d " 1; msg "%s" "red rose that I mean";
pp_print_flush err_formatter ();;
1 red rose that I mean- : unit = ()
#
Related
I want to get the list of numbers present in a file in a specific format. But I did not get any format (like %s %d) for list of numbers.
My file contains text as follows:
[1;2] [2] 5
[45;37] [9] 33
[3] [2;4] 1000
I tried the following
value split_input str fmt = Scanf.sscanf str fmt (fun x y z -> (x,y,z));
value rec read_file chin acc fmt =
try let line = input_line chin in
let (a,b,c) = split_input line fmt in
let acc = List.append acc [(a,b,c)] in
read_file chin acc fmt
with
[ End_of_file -> do { close_in chin; acc}
];
value read_list =
let chin = open_in "filepath/filename" in
read_file chin [] "%s %s %d";
The problem is with the format that is specified towards the end. I used the same code for getting data from some other file, where the data was in the format (string * string * int).
To reuse the same code I have to receive the above text in string and then split according to my requirement. My question is: is there a format like %s %d for a list of integers, so that I get the list directly from the file instead of writing another code to convert string to list.
There is no built-in specifier for lists in Scanf. It is possible to use the %r specifier to delegate parsing to custom scanner, but Scanf is not really designed for parsing complex format:
let int_list b = Scanf.bscanf b "[%s#]" (fun s ->
List.map int_of_string ## String.split_on_char ';' s
)
Then with this int_list parser, we can write
let test = Scanf.sscanf "[1;2]#[3;4]" "%r#%r" int_list int_list (#)
and obtain
val test : int list = [1; 2; 3; 4]
as expected. But at the same time, it was easier to use String.split_on_char to do the splitting. In general parsing complicated format is better done with
a regexp library, a parser combinator library or a parser generator.
P.S: you should probably avoid the revised syntax, it has fallen into disuse.
I am quite new to OCaml, so I am not sure what the following error message means (specifically the /0 and the /-1):
Error: This expression has type (string, string) Hashtbl/0.t
but an expression was expected of type ('a, 'b) Hashtbl/-1.t
I am passing a Hashtbl.t into Hashtbl.find and this error shows up. I am unclear as to how the /0 and /-1 came in, and what they actually mean.
Here's a minimal working example to demonstrate my issue:
open Core_kernel.Std
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str))
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
Let's show you an example :
If I write
type t = A;;
let x = A;;
type t = B;;
let y = B;;
x = y;;
Error: This expression has type t/1561 but an expression was expected of type
t/1558
This is because in the interpreter you can declare multiple types with the same name and associate values to these types. But here, as you can see, x and y are not of the same type but both the types are named t so the interpreter tries to tell you the types are both named t but are not the same.
[Compilation]
If I wanted to compile this, I would have to declare
typea.ml
type t = A
let x = A
typeb.ml
type t = B
let y = B
main.ml
open Typea
open Typeb
x = y
If I compile this I will have
Error: This expression has type Typeb.t
but an expression was expected of type Typea.t
What lesson should you learn from this ? Stop interpreting, compile !
Now that I managed to compile your file, I got an error too but much more explicit :
Error: This expression has type (string, string) Hashtbl.t
but an expression was expected of type
('a, 'b) Core_kernel.Std.Hashtbl.t =
('a, 'b) Core_kernel.Core_hashtbl.t
[Explanation and correction]
Since I'm too nice, here is your file corrected :
let file_to_hashtbl filename =
(* open the namespace only where needed *)
let open Core_kernel.Std in
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val =
try
Hashtbl.find ht "t1"
with Not_found -> assert false
let () = print_endline t1_val
Your error was that you opened Core_kernel.Std as a global namespace so when you wrote Hashtbl.find it looked first in Core_kernel.Std and not in the standard library.
What I did is open Core_kernel.Std in the function that needs it, not in the whole file (so it's a local namespace) (a good habit to take).
So, as you can see, the problem was that you had two definition of the type Hashtbl.t (one in Core_kernel.Std and one in the standard library) and OCaml ain't no fool, boy, he knows when you're wrong but he is hard to understand since he only speak for those who can hear. :-D
P.S. : You had an error in your Hashtbl.find because it doesn't return an option but the found value or raise a Not_found exception if no value was found. I corrected it too. ;-)
Apparently, it is just a matter of missing semi-columns, the foloowing code compiles :
open Core_kernel.Std;;
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
But, I do not know how to interpret the error message neither.
For example, there is a function that testing if a list is monotonically increasing, the source code and testing cases is:
open Printf
let rec mon_inc (numbers : int list) : bool =
match numbers with
| [] -> true
| _ :: [] -> true
| hdn :: tln -> (hdn <= (List.hd tln)) && mon_inc(tln)
let a = [1;2;5;5;8]
let b = [1;2;5;4;8]
let c = [8]
let d = []
let e = [7;8]
let () =
printf "The answer of [1;2;5;5;8]: %B\n" (mon_inc a)
let () =
printf "The answer of [1;2;5;4;8]: %B\n" (mon_inc b)
let () =
printf "The answer of [8]: %B\n" (mon_inc c)
let () =
printf "The answer of []: %B\n" (mon_inc d)
let () =
printf "The answer of [7;8]: %B\n" (mon_inc e)
Compile and run the code:
$ corebuild inc.native
$ ./inc.native
The answer of [1;2;5;5;8]: true
The answer of [1;2;5;4;8]: false
The answer of [8]: true
The answer of []: true
The answer of [7;8]: true
However, when I want to use this function in utop, it shows:
utop # #use "inc.ml";;
File "inc.ml", line 7, characters 29-40:
Error: This expression has type int option
but an expression was expected of type int
This is probably due to your toplevel opening Core, which provides a List.hd that returns an option. In this particular case you can resolve the issue by changing how you match to remove the List.hd entirely:
let rec mon_inc = function
| []
| _::[] -> true
| x::y::rest -> x <= y && mon_inc rest
This is because you have opened Core.Std module in a top-level.
Core.Std is an overlay over a OCaml's standard library with a different interface. For example, in a standard library function List.hd returns a value of type 'a and raises an exception if list is empty. In Janestreet's version function List.hd has a different type - it returns 'a option, it evaluates to None if the list is empty, and to Some value if it is not. Consider adding
open Core.Std
to the top of inc.ml.
I have a function save that take standard input, which is used individually like this:
./try < input.txt (* save function is in try file *)
input.txt
2
3
10 29 23
22 14 9
and now i put the function into another file called path.ml which is a part of my interpreter. Now I have a problem in defining the type of Save function and this is because save function has type in_channel, but when i write
type term = Save of in_channel
ocamlc complain about the parameter in the command function.
How can i fix this error? This is the reason why in my last question posted on stackoverflow, I asked for the way to express a variable that accept any type. I understand the answers but actually it doesn't help much in make the code running.
This is my code:
(* Data types *)
open Printf
type term = Print_line_in_file of int*string
| Print of string
| Save of in_channel (* error here *)
;;
let input_line_opt ic =
try Some (input_line ic)
with End_of_file -> None
let nth_line n filename =
let ic = open_in filename in
let rec aux i =
match input_line_opt ic with
| Some line ->
if i = n then begin
close_in ic;
(line)
end else aux (succ i)
| None ->
close_in ic;
failwith "end of file reached"
in
aux 1
(* get all lines *)
let k = ref 1
let first = ref ""
let second = ref ""
let sequence = ref []
let append_item lst a = lst # [a]
let save () =
try
while true do
let line = input_line stdin in
if k = ref 1
then
begin
first := line;
incr k;
end else
if k = ref 2
then
begin
second := line;
incr k;
end else
begin
sequence := append_item !sequence line;
incr k;
end
done;
None
with
End_of_file -> None;;
let rec command term = match term with
| Print (n) -> print_endline n
| Print_line_in_file (n, f) -> print_endline (nth_line n f)
| Save () -> save ()
;;
EDIT
Error in code:
Save of in_channel:
Error: This pattern matches values of type unit
but a pattern was expected which matches values of type in_channel
Save of unit:
Error: This expression has type 'a option
but an expression was expected of type unit
There are many errors in this code, so it's hard to know where to start.
One problem is this: your save function has type unit -> 'a option. So it's not the same type as the other branches of your final match. The fix is straightforward: save should return (), not None. In OCaml these are completely different things.
The immediate problem seems to be that you have Save () in your match, but have declared Save as taking an input channel. Your current code doesn't have any way to pass the input channel to the save function, but if it did, you would want something more like this in your match:
| Save ch -> save ch
Errors like this suggest (to me) that you're not so familiar with OCaml's type system. It would probably save you a lot of trouble if you went through a tutorial of some kind before writing much more code. You can find tutorials at http://ocaml.org.
Rust has a linear type system. Is there any (good) way to simulate this in OCaml? E.g., when using ocaml-lua, I want to make sure some functions are called only when Lua is in a specific state (table on top of stack, etc).
Edit: Here's a recent paper about resource polymorphism relevant to the question: https://arxiv.org/abs/1803.02796
Edit 2: There are also a number of articles about session types in OCaml available, including syntax extensions to provide some syntactic sugar.
As suggested by John Rivers, you can use a monadic style to represent
"effectful" computation in a way that hides the linear constraint in
the effect API. Below is one example where a type ('a, 'st) t is
used to represent computation using a file handle (whose identity is
implicit/unspoken to guarantee that it cannot be duplicated), will
product a result of type 'a and leave the file handle in the state
'st (a phantom type being either "open" or "close"). You have to use
the run of the monad¹ to actually do anything, and its type ensure
that the file handles are correctly closed after use.
module File : sig
type ('a, 'st) t
type open_st = Open
type close_st = Close
val bind : ('a, 's1) t -> ('a -> ('b, 's2) t) -> ('b, 's2) t
val open_ : string -> (unit, open_st) t
val read : (string, open_st) t
val close : (unit, close_st) t
val run : ('a, close_st) t -> 'a
end = struct
type ('a, 'st) t = unit -> 'a
type open_st = Open
type close_st = Close
let run m = m ()
let bind m f = fun () ->
let x = run m in
run (f x)
let close = fun () ->
print_endline "[lib] close"
let read = fun () ->
let result = "toto" in
print_endline ("[lib] read " ^ result);
result
let open_ path = fun () ->
print_endline ("[lib] open " ^ path)
end
let test =
let open File in
let (>>=) = bind in
run begin
open_ "/tmp/foo" >>= fun () ->
read >>= fun content ->
print_endline ("[user] read " ^ content);
close
end
(* starting with OCaml 4.13, you can use binding operators:
( let* ) instead of ( >>= ) *)
let test =
let open File in
let ( let* ) = bind in
run begin
let* () = open_ "/tmp/foo" in
let* content = read in
print_endline ("[user] read " ^ content);
close
end
Of course, this is only meant to give you a taste of the style of
API. For more serious uses, see Oleg's monadic
regions examples.
You may also be interested in the research programming language
Mezzo, which aims to
be a variant of ML with finer-grained control of state (and related
effectful patterns) through a linear typing discipline with separated
resources. Note that it is only a research experiment for now, not
actually aimed at users. ATS is also relevant,
though finally less ML-like. Rust may actually be a reasonable
"practical" counterpart to these experiments.
¹: it is actually not a monad because it has no return/unit combinator, but the point is to force type-controlled sequencing as the monadic bind operator does. It could have a map, though.