Reputation: 5069
I'm trying to work with the ocaml-inotify package. The relevant parts for this question can be defined as follows
module Inotify : sig
type wd
val int_of_wd : wd -> int
end = struct
type wd = int
let int_of_wd wd = wd
end
This work is taking place in a setuid script, and I want the inotify parts to be handled unprivileged, so I'm forking and then setuid-ing down to an unprivileged user in the child. However, this means that I need to pass the wd entities back to the parent through a pipe, and so need to serialise and deserialise them, requiring a int_to_wd
function.
I've tried extending the module as follows:
module Rich_Inotify : sig
include module type of Inotify with type wd := int
val wd_of_int : int -> wd
end = struct
include Inotify
let wd_of_int (wd:int) : wd = wd
end
module Inotify = Rich_Inotify
However, the compiler complains that wd
is an int
and not a wd
. How do I persuade it that these types are the same?
Upvotes: 2
Views: 878
Reputation: 31459
If the Inotify
module is really defined with this signature, that is sealed with an abstract type, there is no way you will be able to break its abstraction to re-define it as an int. I mean that your signature (module type of Foo with wd := int)
is clever and describes the interface you want, but the implementation Foo
does not satisfy it: if the type-checker allowed that, there would be no type abstraction at all.
You should request the ocaml-inotify
maintainer to add marshalling primitives (and possibly fork it locally to do that if you need). Instead of exposing the fact that wd = int
, he or she could either publish conversion functions to and from int
(so that future implementation changes could still implement these functions), or directly to and from string
if you're only interested in marshalling and want to expose less internal details.
There is a solution that exposes more internal details that back-and-forth conversions to an abstract type, which is to use private type abbreviations:
sig
type t = private int
val mk : int -> t
end
This signature exposes that the internal representation is int
, and allows to cast from t
to int
explicitly with (foo :> int)
or (foo : t :> int)
(you know that this is a no-op at runtime), but does not allow the inverse cast, so you are forced to use the mk
function which, I assume, will do some kind of range checking to make sure that this is a valid descriptor.
(Some people will probably suggest that, as a workaround, you break the type safety of the language by using an unsafe cast that should not be named. Do not; this is bad advice.)
Upvotes: 4