pgocaml
pgocaml copied to clipboard
Top-level binding of db handle via PGOCaml.connect fails
let dbh = PGOCaml.connect ()
Fails with the following error:
Error: The type of this expression, '_a PGOCaml.t PGOCaml.monad,
contains type variables that cannot be generalized
However, pushing this call one layer deeper, as in
let () = let dbh = PGOCaml.connect () in ()
Will get rid of the error.
Yeah, the compiler is complaining that the type variable is weakly polymorphic. This is not really a problem: you can either add a type annotation, or -- most likely -- the compiler will be able to infer the type if you put the declaration in a real-world context.
I was not able to have the compiler infer the type no matter what I tried. I'm not sure which kind of type should be used for the annotation either, or if it matters. I think this should qualify as a documentation bug at least.
@Giels: can you provide a real world example where this is a problem?
(Also, in case you haven't done so already, please read this chapter of Real World OCaml).
Beside the surprise at the behavior, consider the case where one would like to define functions which, given a handle, execute prepared statements. But obviously it's not possible to prepare statements without a database handle. However, it is a bug to attempt to execute prepared statements that haven't been initialized yet. It is then not possible to obtain a global handle and prepare statements as part of an imperative-style initialization procedure. The database handle has to be wrapped to ensure only one in a valid state can be used to perform queries, even though that wrapping is useless as soon as the initialization process happens since any further reference, old or new, to the same open database, will have the prepared statements ready.
@darioteixeira Obviously this issue isn't a real bug but I think it does bring an interesting point. I worked in a team with beginning OCaml programmers and I did have to explain this exact issue to them. So I do think it's a minor usability issue.
Moreover, I think that we can improve pgocaml further not to require this type parameter. It seems to me that what camlp4 extension stores in there does in fact have a concrete type (a hashtable from strings to booleans IIRC). Would it be possible to just set the type of private data to that and be done with it?
Yes this leaks a little implementation detail, but given that this whole thing is only useful for the camlp4 extension I think it's a decent trade off.
@darioteixeira Why is 'a monad not covariant ? It would help a bit ...
@Drup: You're right, there's a chance users may bump into the value restriction, in which case marking the type as covariant would help. I still wonder if there's any real world example where it actually matters, though...
@darioteixeira What about getting rid of the type variable altogether? There's definitely some real world code where this annoyed me.
Here's a use case where the extra type variable is annoying. If you define some type that contains a PGOCaml connection handle, you must now add a type variable to your type as well. This is already moderately annoying, but suppose your type already has a type variable and you'd like to apply a functor to that type as well, now you can't even do that.
The former situation has already occurred to me, and I'm not eagerly waiting for the latter either.
The case of the extra type variable is where PG'OCaml's convoluted history rears its ugly head. I didn't write that particular code, and I can only speculate about the original motivation for adding the type variable. Anyway, though I'm sure there were good reasons, I'm not wedded to the type variable, and if @Giels and @rgrinberg would rather see it go, then it goes. (I'll push the code soon.)
EDIT: The type variable is there because the API allows setting/retrieving user-defined private data...
Hmm, I've never used the setter/getter functions for private data, but I cannot exclude the possibility that some people are using them. I'll raise the issue on the mailing-list, because removing this functionality could break existing code.