Do not trigger the "missing rec" hint for simple values#1678
Do not trigger the "missing rec" hint for simple values#1678xclerc wants to merge 1 commit intoocaml:trunkfrom
Conversation
|
Ping @Armael :) |
gasche
left a comment
There was a problem hiding this comment.
This patch sounds like a good idea to me, and it contains an important fix to fold_values that you did not mention in your description.
I think that it would make sense to relax "of function type" to "of function type or lazy type".
Are we sure that the support for let .. and ... (multiple in the patch) is worth it? It makes the patch more complex, changes the type declarations, and I am not convinced that it is actually that useful. It would pay for its cost if mutual value recursions were somewhat common, but are they?
Finally, at some point I thought that this patch would also silence useful-warnings, and I had a type signature such as type 'a seq = Seq of (unit -> ('a * 'a seq) option) in mind; there would be recursive declarations, I thought, at the type 'a seq, starting with the Seq constructor, and the warnings would be silenced by this patch. In fact it is almost impossible to build a useful sequence without maintaining some state as an argument of the recursive call, so the recursion happens on a function type:
let nats = nats_from 0
and nats_from n = Seq (fun () -> Some (n, nats_from (n+1)))| and value_unbound_reason = | ||
| | Val_unbound_instance_variable | ||
| | Val_unbound_ghost_recursive | ||
| | Val_unbound_ghost_recursive of bool |
There was a problem hiding this comment.
If we keep this aspect of the patch, I would prefer to see an interface that more explicitly gives a meaning to the bool payload, for example using a record type (possibly an inline record) with a field name. Currently it is very unclear to a reader of the .mli what this bool would mean.
There was a problem hiding this comment.
Sure; I wanted to push this draft to get some feedback
and explore the design space. For instance @lpw25
suggested that we could use the recursive check to ensure
that the hint actually makes sense.
| (fun name _path descr acc -> | ||
| match descr.val_kind with | ||
| | Val_unbound _ -> acc | ||
| | _ -> f name acc) |
There was a problem hiding this comment.
This is an important omission of the previous patch (cc @Armael) that we need to merge independently of the rest, it could have gone into a separate commit.
There was a problem hiding this comment.
I suspect this was not detected earlier because in the case of an unbound value with a corresponding ghost binding, the "missing let rec" hint would trigger, and therefore no spellchecking would be attempted. Only the present patch adds the possibility of not printing the hint, and uses this -- buggy -- codepath. Am I correct?
In any case I agree this fix is required.
There was a problem hiding this comment.
I am not completely sure, but I think @Armael is right.
Do you mean "a rec may be missing" hints, instead of "warnings"? |
|
@gasche I didn't understand fully your point about |
Indeed, but I am not completely sure it could be observed in presence
Maybe. Are you referring to the "trick" using lazy on the right-hand side
(I should have commented this aspect in my original post.) |
|
@Armael my point was that you can have recursion at datatypes that are not functions but contain functions, but then I realized that the recursive definitions usually occur at function types (as in my example) even when we are building one of these datatypes. @xclerc there is something odd in the "type variable" story that I would like to understand better. Are the typing environments populated differently for
More generally, values that are typically recursive have a function or a lazy type: |
|
Well, my understanding of the type checker is far too limited |
|
Btw, the idea of using the recursive check on the hint sounds tempting. But I wonder how much refactoring work it would take to plug it at this stage? |
Maybe @lpw25 or @garrigue can shed some light on this question? Personally, I was expecting the reason for the |
So, I investigated a bit, and I think I can explain (this is mostly guesswork so maybe I'm wrong though). @xclerc describes that this PR enables the hint if "the value is an arrow type". This is a bit imprecise. To be more precise, I think the criteria we want is that, when a ghost identifier appears in the body of a definition, then the missing rec hint is displayed if the body of the definition is a function. However, what this PR currently implements is a bit different: faced with a ghost identifier, it does not check if the type of the enclosing body is a function, but if the type of the ghost identifier is an arrow type. For single let-bindings, this is equivalent. Indeed, in that case, the type of the ghost binding is the same as the type of the definition body it appears in. Then, at the time the typechecking function discovers the ghost identifier, its type has already been refined to an arrow type by inspecting the definition body. For multiple let-bindings, this does not work, as it has been noticed. On an example: Trying to typecheck the body of This I think explains the current behaviour. Idea for a fix: what about associating to a ghost binding the type of its enclosing definition? I guess this would be an argument to the Do you think that would work? |
|
That's a nice analysis, thanks! I'm a bit unsure about adding arguments to Here is a rough idea for an alternative implementation strategy: catch the |
|
Not sure whether it helps but if you changed the to However, as @Armael said, the check is currently checking the type of the value being used, whereas really it is the type of the value being defined that matters. (Also the It is a shame that the current recursive values check is implemented as a separate pass over a typed tree, rather than implemented as part of the main typing pass (which I think would be an equally viable approach) because then you could just check the desired property directly. How about only adding the ghost bindings for let definitions that are obviously functions? You could implement a simple function similar to |
|
I had a discussion about this PR with Armaël a few days ago. Here is a summary of the opinion we converged to:
I encouraged Armaël to submit a PR with the simple changes (2) and (3) above, but he is busy with other things right now. |
|
@gasche Can we maybe revert the change which added this hint and reconsider for 4.08? The dev Flambda branch tracks trunk and whilst working there I have seen many occurrences of this hint all of which, except for one, were wrong. |
|
Well yes, we can certainly do this. Could you list some of those examples of mistaken hints in the discussion of #1472? |
|
I have some time today, I could submit PRs for 2) 3) and 4) if that's judged satisfactory. (on xclerc original example it would display "if this is a recursive declaration..." and then the spellchecking hint (or maybe first the spellchecking hint?)) |
Imported from Xavier Clerc patch in ocaml#1678
Imported from Xavier Clerc patch in ocaml#1678
Imported from Xavier Clerc patch in ocaml#1678
|
I think this PR is now superseded by #1720. Thanks for the problem report! |
Imported from Xavier Clerc patch in ocaml#1678
The "You are probably missing the `rec' keyword" hint is mildly
annoying when triggered for simple values because it is almost
always wrong, and very often suggests a fix that will then trigger
an error. For instance:
This PR slightly changes the condition so that the hint is triggered iff
there are multiples definitions (i.e.
let...and), or the value is anarrow type. The previous toplevel session then becomes: