Lwt: Are comparisons (including equality tests) legitimate on Lwt.t values ?

Created on 11 Oct 2019  路  4Comments  路  Source: ocsigen/lwt

Hi,

(As a preliminary, I am not sure this is the right place for this kind of discussion. Yet, this topic does not seem to have already been considered. Consider it as a low-priority discussion.)

I would like to draw attention to a potential and annoying race-condition using Lwt.

Consider this legitimate code:

let%lwt x1 = some_lwt_value_1 in
let%lwt x2 = some_lwt_value_2 in

if x1 = x2 then bla-bla-bla

Now, for some reason, assume I have forgotten to open the lwt box:

let x1 = some_lwt_value_1 in
let x2 = some_lwt_value_2 in

if x1 = x2 then bla-bla-bla

This code still typechecks, but of course we now compare Lwt.t values.
At run-time, two things may apparently occur:

  1. If both x1 and x2 are resolved, the comparison works as expected.
  2. If x1 or x2 is unresolved, the comparison fails with "compare: functional value".

I think this may be a serious problem. Here is why:

  • This kind of error easily arises when you upgrade a classical function f to a lwt-function.
    let x1 = f y1 and x2 = f y2. If x1 and x2 are only used in comparisons, the typechecker is happy.
    I have just upgraded a dozens of functions in some large code of mine. These functions are precisely used to compare values with each other. So now, I am quite uncomfortable that I might have left a comparison on lwt values.

  • The runtime behavior now depends on x1 and x2 been resolved or not. This is a very unfortunate race-condition. It may not occur during development but only when the code is deployed, due to longer delays in the computation of x1 and x2.

So, here is my question: are comparisons on Lwt.t values legitimate?
As far as I know, it is not possible to forbid this by type-checking. However, I assume it remains possible to forbid them at run-time (efficiently?).

Most helpful comment

Instead of self-discipline, you can also use monomorphic.

All 4 comments

No, comparisons of promises are not supported. They do work right now if promises are both resolved, but I think it's best not to rely on that remaining true, either.

I wouldn't call this a race condition, but it is indeed something that can bite a user.

I can't immediately think of a way to uniformly forbid promise comparison using =, except by inserting dummy function values into every promise. I don't think we should do that, at least not unless this issue affects more users. I'm happy to consider counterarguments :)

Note that, in general, there are many types that have similar problems. For example,

type t = (unit -> unit) option
let a : t = None
let b : t = Some ignore
a = a (* true *)
b = b (* Exception: Invalid_argument "compare: functional value". *)

So this is a problem with polymorphic equality itself. Of course, problems with comparisons at (unit -> unit) option are not likely to arise as a result of refactoring code, so it's not as annoying.

The only thing I can immediately recommend, is to avoid polymorphic comparisons wherever reasonable, and use a type's compare function when it is available. That should both statically catch problems when converting to Lwt, and also help to immediately see that Lwt promises are not comparable (due to the lack of a compare function).

I agree that inserting a dummy function in Lwt promises is very unsatisfactory, even if it does the job. (It would be useful here if OCaml somehow allowed to tag datatypes with an "uncomparable" tag... or had type classes...)

Not using the polymorphic equality is wise... but requires a strong self-discipline (and is unfortunately unaesthetic for base types).

For the moment, I will manually check the consequences of every lwt-refactoring I do, exceptionally not relying on the type-checker (as do C or Python programmers, argh).

Yes, I also use = a lot.

Another thing that can help is occasional type annotations on your lets. You might insert them temporarily while refactoring, and remove them when done.

Instead of self-discipline, you can also use monomorphic.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aantron picture aantron  路  6Comments

ljw1004 picture ljw1004  路  4Comments

foretspaisibles picture foretspaisibles  路  8Comments

fxfactorial picture fxfactorial  路  7Comments

olafhering picture olafhering  路  8Comments