Integer negation operator confusion

I understand that I can coerce from t to int using :> but can I go the other way around?

Is there a way to use coercion here and avoid using of_int here?

module Id : sig
  type t = private int

  val intern : string -> t
  val get : t -> string option
end = struct
  type t = int

  let of_int n = n
  let id = ref (of_int (-1))
  let table = Hashtbl.create 100

  let intern s =
    id := !id + 1;
    Hashtbl.add table !id s;
    !id
  ;;

  let get id =
    try Some (Hashtbl.find table id) with
    | Not_found -> None
  ;;
end

It works fine. Am I missing something?

-  let of_int n = n
-  let id = ref (of_int (-1))
+  let id = ref ~-1

It’s ~ the coercion magic I must have been looking for!

Actually, ~- is no coercion magic but a replacement for of_int.

Do I correctly understand that a function call is required to coerce from int to t?

~- is just integer negation.

# ~-5 = -5;;
- : bool = true
1 Like

You defined t as a synonym to int, so inside the module, the two are completely interchangeable and you don’t need of_int. int and t are literally the same type here.

From outside the module, t is private, so you only get a coercion in one direction. this is intentional because your t might have more invariants than a plain int, that a coercion in the other direction could break.

2 Likes

Try removing of_int in my original code.

It doesn’t work but I don’t understand why.

It does work. I took your original code, removed of_int, and ran it in my REPL locally. It compiled and ran. The functions worked.

Maybe you can clarify what you mean by ‘doesn’t work’.

1 Like

I suspect that @joelreymont is running in the syntactic problem (whose details I never remember after all these years) that sometimes using -1 works and sometimes it doesn’t:

# let x = -1;;
val x : int = -1
# let y = succ -1;;
Error: The value succ has type int -> int
       but an expression was expected of type int
# let y = succ ~-1;;
val y : int = 0

Note the same occurs with floats:

# let x = -1.0;;
val x : float = -1.
# let x = sin -1.0;;
Error: The value sin has type float -> float
       but an expression was expected of type int
# let x = sin ~-1.0;;
Error: The constant 1.0 has type float but an expression was expected of type
         int
# let x = sin ~-.1.0;;
val x : float = -0.841470984807896505

Myself twelve years ago :–)

I never understood this behaviour:

At least you’ll get an explanation:

3 Likes

I personally prefer:

let id = ref  (-1) ;;

to

let id = ref ~-1 ;;

I guess, whatever floats your boat.

1 Like

As @dbuenzli said:

❯ cat /tmp/foo.ml
module Id : sig
  type t = private int

  val intern : string -> t
  val get : t -> string option
  val compare : t -> t -> int
end = struct
  type t = int

  let id = ref ~-1
  let table = Hashtbl.create 100

  let intern s =
    id := !id + 1;
    Hashtbl.add table !id s;
    !id
  ;;

  let get id =
    try Some (Hashtbl.find table id) with
    | Not_found -> None
  ;;

  let compare a b = Stdlib.compare a b
end

The above compiles.

Remove the ~ and it no longer does.

I love the (-1) solution, though!

To understand why that is in OCaml, note that “-” comes with two possible (but related) meanings: first, it connotes application of a binary infix subtraction operator, as in 2 - 1, and secondly it connotes the unary prefix negation operator, as in -1.

In OCaml the ~- operator (for integers) and ~-. operator (for floats) represents the second of those.

So given the application of a function f to an integer argument, the form “f -1” won’t compile as it would be construed as an attempt to subtract an integer from a function. The same applies to “ref -1”. You need the form “f ~-1” or disambiguate with parentheses as in “f (-1)” (and likewise for ref).

Sorry if you already know all this but I wasn’t clear to me that that was the case.

3 Likes

I didn’t know about it until I browsed the issue that @dbuenzli posted. I do appreciate your detailed explanation, though. It will save time for anyone stumbling upon this issue in the future!