1
votes

I want to write a function that implements basic usage of grep: matching a pattern in a file. And I want to match_file_pattern to return a list of matched lines. But the code here cannot compile, the error is:

Error: This expression has type string list but an expression was expected of type unit

And the code is:

let match_file pattern file_name =
  let matched_lines = ref [] in
  let ic = open_in file_name in

  try
    while true
    do
      let line = input_line ic in
      if (Str.string_match (Str.regexp pattern) line 0)
      then
         matched_lines := line::!matched_lines
    done;**(*!matched_lines*)**(*I need add this to compile successfully*)
  with End_of_file ->
    close_in ic;
    List.rev !matched_lines;;

I think the error is caused by in ocaml, close_in ic; List.rev !matched_lines is grouped into a subexpression of "with" keyword, so its type should match with "try" expression. I try to find ways to break the relation between close_in ic; and List.rev !matched_lines, but failed.

3

3 Answers

2
votes

You can use begin/end or parentheses:

let match_file pattern file_name =
  let matched_lines = ref [] in
  let ic = open_in file_name in

  begin
  try
    while true
    do
      let line = input_line ic in
      if (Str.string_match (Str.regexp pattern) line 0)
      then
         matched_lines := line::!matched_lines
    done
  with End_of_file -> close_in ic
  end;
  List.rev !matched_lines
1
votes

The type of the loop is unit even though it never complete. The typechecker doesn't know that, so you need to make the expression under try have the same type as the exception handlers.

In this case you could use an arbitrary list such as [] but it's misleading for the reader, and doesn't generalize to cases where it may be more complicated to provide an expression of the correct type.

The idiomatic solution here is to place an assert false, which would raise an exception if ever evaluated. Unlike the infinite while loop, assert false is known by the typechecker to not return and it is compatible with any type since a value is never produced:

try
  while true do
    ...
  done;
  assert false
with ... -> ...
0
votes

Your code is just fine:

one semicolon after done, for sequencing of instructions, and then !matched_lines as return value of the try code part, then with ....

No ambiguities here. The compiler just doesn't take into account that End_of_file is always raised.

The rest is question of coding style. I like putting a (* never reached *) comment at these technically required expressions - would be a good idea for the assert false proposal as well IMO.