Stdio.printf giving type error while Printf.printf does not

62 views Asked by At

A 'small' ocaml printf issue. There is a printf in module Printf that works in utop and even ocaml (interpreter) and with ocamlc (compiled) There is printf in module Stdio. that only works in utop and ocaml (interpreter) but not in compiled code.

When I compile the code with this: camlopt -I ocamlfind query stdio -o penelem penelem.ml The Printf.printf version works just fine but the Stdio.printf version fails with:

Error: This expression has type string but an expression was expected of type ('a -> 'b, Stdio.Out_channel.t, Base.unit) Base.format Base.format is abstract because no corresponding cmi file was found in path.

Supposedly Printf.printf is being deprecated at some point in favor of Stdio.printf (according to compiler messages). So I'm assuming they're trying to move everybody over to Stdio.printf eventually. If anybody has used Stdio.printf extensively I have been thinking over the complicated type errors but am still stymied as to the exact statement construction for this function. Further examples would be appreciated. Why aren't there any good ones in the Stdio.printf documentation? Seeing an example is easier than thinking too hard about it!

Here is the simple code from an ocaml intro course:

open Stdio
(* open Printf *)

let rec sum lst = 
  match lst with 
  | [] -> 0
  | h :: t -> h + sum t ;;


let numbers = [2; 8; 4; 5; 9] ;;

let result = sum numbers ;; 

let () = Stdio.printf "About to print some numbers ...\n" ;;

let () = Stdio.printf  "Sum =  %d\n%!" result  ;;

 let name = "Alice" in printf "Hello, %s\n%!" name


let rec print_list lst = 
  match lst with
  | [] -> printf "\n" ;
  | head :: tail -> 
      printf "%d " head ; 
      print_list tail ;;


let () = print_list numbers ;;

Switched back and forth between Stdio.printf and Printf.printf Put a double semicolon after every statement to isolate them.

From Stdio.printf and Printf.print I was expecting the same output from interpreted and compiled code. The exception is expected and is part of the example.

#use "penelem.ml" ;;
val numbers : int/2 list/2 = [1; 8; 2; 5]
val words : string/2 list/2 = ["ocean"; "water"; "sea"]
val before_last : 'a list/2 -> 'a = <fun>
penultimate element of numbers is: 2
penultimate element of words is: water
Penultimate element of [1,2] is 1
Exception: (Failure "Singleton List -> No Penultimate Element").
Raised at Stdlib.failwith in file "stdlib.ml", line 29, characters 17-33
Called from penum in file "penelem.ml", line 35, characters 12-27
Called from Topeval.load_lambda in file "toplevel/byte/topeval.ml", line 89, characters 4-14

Of course Stdio.printf gave a type error message instead of the expected output.

2

There are 2 answers

1
octachron On

First, the Printf module from the standard library is not getting deprecated at all. The message that you are alluding at is probably just the base alternative standard library being opinionated about Printf.

Second, the type error in your example is due to a misuse of ocamlopt: all transitive dependencies must be included with -I ... when calling ocamlopt directly as explained by the last part of the type error message

ocamlopt -I $(ocamlfind query stdio) program.ml
Base.format is abstract because no corresponding cmi file was found in path.

Indeed, since you are not including the base library cmis, the compiler can only make the hypothesis that the Base.format type is abstract.

Third, I would advise you to use dune (or at the very least ocamlfind ocamlopt -package) to compile your programs if your objective is not to write build systems for OCaml:

ocamlfind ocamlopt -package stdio -linkpkg program.ml

would work, or just

dune build

with a dune file with the right dependencies.

2
Chris On

Your comment about double semicolons indicates you have a serious misunderstanding about the structure of an OCaml program. Every OCaml program involves expressions and declarations. At the top level of your program you should only have declarations.

let bar = "foo"

Is fine, because it is a declaration. The right hand side of that declaration is an expression, and the left hand side a pattern.

The ;; token is not necessary between top level declarations, and since that's all you should have at the top level of your program, you should never need to use them.

Ironically, you have exactly one bare expression at the top level of your program, and you have not used ;; to disambiguate this.

let name = "Alice" in printf "Hello, %s\n%!" name

This should give you a syntax error as it does in the below simple example.

# let name = "Alice" in Printf.printf "%s\n" name
  let foo = 42;;
Error: Syntax error

If we wanted to convert this to a declaration:

let () = 
  let name = "Alice" in
  printf "Hello, %s\n%!" name

Now the left hand side is a pattern (()) and the right hand side is the expression:

  let name = "Alice" in
  printf "Hello, %s\n%!" name