Skip to content
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

All-float records and abstractions don't mix #4584

Closed
vicuna opened this issue Jul 18, 2008 · 4 comments
Closed

All-float records and abstractions don't mix #4584

vicuna opened this issue Jul 18, 2008 · 4 comments
Assignees

Comments

@vicuna
Copy link

vicuna commented Jul 18, 2008

Original bug ID: 4584
Reporter: @mmottl
Assigned to: @alainfrisch
Status: closed (set by @xavierleroy on 2013-08-31T10:43:55Z)
Resolution: duplicate
Priority: normal
Severity: minor
Version: 3.10.2
Category: ~DO NOT USE (was: OCaml general)
Duplicate of: #4423
Monitored by: BenediktGrundmann sweeks till "Julien Signoles" @Chris00 @oandrieu @alainfrisch @mmottl

Bug description

Consider the following file foo.ml:

type t = float
type u = { t : t }

together with the following foo.mli:

type t
type u = { t : t }

Trying to compile these will give the following very unintuitive error:

The implementation foo.ml does not match the interface foo.cmi:
Type declarations do not match:
type u = { t : t; }
is not included in
type u = { t : t; }

Replacing "float" with e.g. "unit" will make this module compile just fine. The reason is quite obviously the well-known fact that OCaml uses a more efficient unboxed representation for float records.

However, this kind of error message is hopelessly confusing, especially when it happens in the presence of nested modules, where it may not be easy for humans to see that they are dealing with an all-float record. Furthermore, OCaml seems slightly inconsistent here, because float arrays, too, are represented in an unboxed way, but using an array instead of a record in the above code seems to work fine with floats.

It would be great if the compiler could use the same approach for dealing with all-float records as it already does with arrays when accessing elements, i.e. if the compiler cannot prove at compile time whether or not some record is all-floats, it will have to generate code to perform a runtime check. In most cases the compiler will be able to avoid generating such a check anyway. At least giving more details in the error message why unification fails might otherwise be helpful.

@vicuna
Copy link
Author

vicuna commented Jul 24, 2008

Comment author: @alainfrisch

I agree the situation is not satisfying and that at least the error message should be improved. But I would not like to see a solution arrays implemented for records. Note that the solution would be more complicated than for arrays anyway, because it might be needed to check several fields when the record is created (there might be several type arguments to the record's definition); also, supporting the {... with ...} notation efficiently would not be trivial.

Actually, one could argue that the solution for arrays is not itself very satisfying: any polymorphic function on arrays suffers from runtime checks, and we loose the nice property that reading from an array does not allocate memory (and thus cannot trigger the GC, for instance; the same problem holds for all-floats records, btw). Also, it adds constraints to the way values are represented and complexity to the runtime system: for instance, because of float arrays, it not possible to represent a forced "float lazy" as a float in memory.

For records, I think a good solution would be to add a syntactic marker to the record definition to indicate that all fields are unboxed floats (the fact that fields are indeed floats is checked when the record type is declared). I don't think that automatically unboxing a parametrized record type when parameters happens to be floats by coincidence actually matters in real code (for intensive numerical code, we would probably use other data structures anyway or at least be willing to define custom record types).

For instance, one could write:

module M: sig
type t
type s = {x:t; y:float} as float
end = struct
type t = float
type s = {x:t; y:t} as float
end

and removing any of the two "as float" annotation would yield a type-error.

(Implicitly, the signature implies that t is a float but we cannot use this information.)

@vicuna
Copy link
Author

vicuna commented Jul 24, 2008

Comment author: @alainfrisch

(Related to #4423)

@vicuna
Copy link
Author

vicuna commented Aug 6, 2008

Comment author: @xavierleroy

I wholeheartedly agree that the error message should be improved, and also for other cases of mismatch between two type declarations. I'm not sure it can be done for release 3.11 but I'll look into it.

The current approach to data representation for records isn't perfect, but I believe it is the least of several evils. Extending the trick used for arrays is problematic as Alain said.

The "private type abbreviations" feature of 3.11 can alleviate this issue. If you give the interface foo.mli as

type t = private float
type u = { t: t }

the compiler "sees" just enough in the definition of "t" to select the proper representation of "u" within clients of Foo. Yet, most of the benefits of type abstraction are preserved.

@vicuna
Copy link
Author

vicuna commented May 19, 2011

Comment author: @alainfrisch

Less confusing error message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants