New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incoherent syntax between types and functions #7149
Comments
Comment author: @gasche Since 4.02.2, OCaml supports a "nonrec" keyword in type declaration precisely for this: type t = A | B;;type t = A | B type nonrec t = C of t | D;;type nonrec t = C of t | D C A;;
Note that with older OCaml versions you can often work around the lack of "nonrec" by declaring type synonyms: type t = A | B;;type t = A | B type u = t;;type u = t type t = C of u | D;;type t = C of u | D C A;;
or use modules for scoping module M = struct
end;; include M;;type t = M.t = A | B type t = C of M.t | D;;type t = C of M.t | D C A;;
|
Comment author: MonsieurPi Note that with older OCaml versions you can often work around the lack of "nonrec" by declaring type synonyms: type t = A | B;;type t = A | B type u = t;;type u = t type t = C of u | D;;type t = C of u | D C A;;
I don't like the use of synonyms because I don't want the first t to be visible (even with the alias u). For the first solution, I didn't know it but it still remains incoherent (I think) with the function declaration. But well, at least the feature exists ;-) Thanks |
Comment author: @gasche
It's hard to evolve a language -- and I find your remark a bit insensitive in that it ignores this difficulty. ML historically had both recursive and non-recursive let-bindings (using "let" and "let rec"), but only recursive type bindings (using "type"). The situation since 4.02.2 is more symmetric, as we also have recursive and non-recursive type binding (using "type" and "type nonrec"). My personal preference would be to have "rec" and "nonrec" available for all binders (so we could write "let nonrec x = 1" and "type rec ulam = Lam of ulam -> ulam", and also "module rec" and "module nonrec", ec.), and have a "default recursivity", for each binder, depending on which form is used more often. For "let", "nonrec" would be the default, so a simple "let" would be understood as "let nonrec", and "type" would be "type rec". This is consistent and convenient. In practice adding nonrec was already a controversial move in terms of backward compatibility, and the language was extended minimally (no support for "type rec", no support for "let nonrec") to minimize unplanned consequences. |
Comment author: MonsieurPi
Oh, yes, sorry. I thought about retro-compatibility and I didn't want to sound insensitive because I know it's hard. What I was trying to say is that, for example in case of teaching, this kind of incoherence can sometime be a blocking moment. But I guess it's like your brother in law, you have to deal with it. :-D I completely agree with your personal preference. :-) |
Comment author: MonsieurPi Nevertheless, it's not working as expected If I write let f x = x + 1;;val f : int -> int = let f x = x +. 1.;;val f : float -> float = and then f 3;;Error: This expression has type int but an expression was expected of type the first declaration of f is hidden. Now, if I write type t = A | B;;type t = A | B type nonrec t = C of t | D;;type nonrec t = C of t | D And, then A;;
C A;;
And even worse : let f = function
Error: This variant pattern is expected to have type t |
Comment author: @gasche It seems to me that this is working as expected. I'm not sure what you mean by "make the first t unavailable"; if the type is unavailable, should its constructors also be unavailable? But then, how would you expect to use the (C of t) constructor of the second type, if the first is "unavaiable"? You could try using module M : sig type t end = struct which makes t an abstract type (its definition is not exported in the signature of M). You cannot talk about A or B outside M, but that severely restricts the usefulness of the second type t. |
Comment author: MonsieurPi Mmmh, I would agree with you but what I wanted to do was the following : Define an algebraic type t then define some functions to handle it Define another algebraic type t then define some functions to handle it. What I'm sure of, at this point, is that the first defined type t can not be managed by other functions than the previously defined ones. For example type t = A | B let f = function let create i = if i > 0 then A else B;; type nonrec t = C of t | D let create i j = if j > 0 then C (create i) else D;; so, the previous defined functions f and create are not visible, only the ones defined for the second type. I don't have to create a module for this. But at this moment, I can still do f (C A);; and I expect ocaml to tell me Error: This variant expression is expected to have type t (which is what happens if I write let f x y = x + y;;val f : int -> int -> int = let f x = f x 1;;val f : int -> int = f 2 3;;Error: This function has type int -> int |
Comment author: @gasche We have other ways to refer to a type than its name (for example, we can use its constructors); this mean that hiding or shadowing the name does not suffice to prevent a type from being used. If you want to ensure that "the first defined type t can not be managed by other functions than the previously defined ones", you have to create a module, put the functions in this module, and use the module interface to enforce your preferred usage restrictions. (It is possible to allow matching over values of the type, but not creating new values, using "private" types. See the OCaml manual for more details.) |
Comment author: MonsieurPi Ok. I agree with that. I still find strange that we have to types named t. Didn't know this was possible. Thanks for all the answer, I'll keep using my modules, then ;-) |
Original bug ID: 7149
Reporter: MonsieurPi
Assigned to: @gasche
Status: closed (set by @xavierleroy on 2016-12-07T10:29:43Z)
Resolution: not a bug
Priority: low
Severity: feature
Fixed in version: 4.02.2
Category: ~DO NOT USE (was: OCaml general)
Monitored by: @diml @yallop
Bug description
To declare a recursive function, we need to write
let rec f x = ... (* some call to f *)
To declare a type (recursive or not) we always write
type t = ... (* here we can mention t *)
My problem is this :
If I want to write
type t = A | B
type t = C of t | D
which would allow me to make the first type t unavailable outside of the second type, I can't do it.
Maybe I could write
type t = let type t = A | B in C of t | D
but no, it's not allowed.
First of all, the declaration of recursive type is not coherent with the declaration of recursive functions and, second, the absence of a "rec" keyword doesn't permit me to do whatever I want with types :'(
The text was updated successfully, but these errors were encountered: