-
Notifications
You must be signed in to change notification settings - Fork 22
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
Implicit types on record elements #747
Comments
How would you create an instance of such a record? |
Such as described in the first code example? You just leave it free, if you like to infer it. |
No, the first code example is defining the type. I'm asking for the equivalent code for { Name = "John Doe"; Description = "An ordinary man" } . Will this be written the same way? |
I think you mean the existing way is to do this is: type Details =
{ Name : Name
Description : Description } You might want to edit that to avoid confusion. I don't think this is a bad idea. But I don't think it's worth introducing to F# yet another instance of multiple ways to do the same thing, and to be slightly less explicit, just to save a few characters. It's another thing for people new to the language to learn that they won't see very often because it's only in code using the latest versions of F#. Note: One way of getting something similar but not quite the same with existing features is to use a single-case DU and tuple. It's not as convenient to access the component parts as it is with records. type Name = Name of string
type Description = Description of string
type Details = Details of Name * Description |
@Happypig375 Yes, correct. At least this is my impression. It scares the hell out of me in general, while less so in F#. This whole topic goes beyond this suggestion and I think it's important since people get used to custom types. Primitive types could be a compilation detail, nobody needs to deal with them in the code. YMMV |
So you have type Details =
{ Name
Description }
let a : string = "John Doe"
let b : Details = { Name = a; Description = "An ordinary man." }
let c : Name (* or Details.Name? *) = b.Name For |
I had to upgrade my internal compiler to understand this thread. The title and first paragraph mention a way to conflate record member names and type names. So "on the surface" this request has type However subsequent discussion implies that it is actually has type |
To be fair I also thought this was just to save a few keystrokes until I read the Optional Optimization part. |
@ShalokShalom Please don't modify the affidavit section that previously said this:
To be clear, would you or your company be willing to implement this? This suggestion is currently two suggestions:
The first suggestion, based on subsequent discussion, is more in line with existing F# semantics, like this: let add x y = x + y // 'x' and 'y' are inferred to be 'int', as is the result Such behavior has a precedent with existing F# code and its purpose is primarily to save keystrokes. However, it is the body of the function that helps the typechecker determine a type to infer. The only meaningful types that could be inferred for records without any members that perform some kind of algebraic operations on them is a generic type. type R = { A; B }
// record R
// val A: 'a
// val B: 'b This might be useful for defining generic structural groups of data, but unlike functions, do not allow for using type inference + The second suggestion does not have any existing precedent. The closest I can think of is variable name suggestion via Humanizer in the C# tools for Visual Studio. I don't think the section about optimization is relevant here. This wouldn't improve CPU time for compilation in any way; it would probably make it marginally worse. I don't think I'm in favor of this, but I'm certainly open to examples where this would be useful. What uses would there be to declaring generic, nominal groups of named data? |
I'm not so sure about this myself. Seems like it adds another way of doing things that potentially leads to some very bad habits. A question I'd be asking is we use records far more than we define them, so what kind of use case is defining so many record types that they need a shorthand. If there is one perhaps we should be focusing on adding records to type providers or some other code gen story. Here's an example of a dangerous habit this proposal affords. type R = {Int; Float; Bool} |
Hi there all 🤗 Thanks for your participation. Reading through these comments, I think I made a not so good job describing what I mean:
@voronoipotato Correct. I suggest this now as an addition to record types, since record types are one of the things a newbie sees first and we can well apply this to other constructs as well. Why this all? The general idea is just, that we use this concept as the default way in order to increase the usage of custom types, compared to primitives. In order to sustain the assumed performance benefit of build in types, I suggested the 2 layer principle. In general is the fundamental idea the same as that for type inference, significant whitespace and so on. Why typing something that is obvious? Don't repeat yourself. 🤗 |
I better understand what you are trying to accomplish. I think it makes sense why you would want it. I wonder if a tuple type alias or single case DU will accomplish your goal. We try to reduce the number of ways of doing the same thing if possible. type Details = Name * Description If you want to exclude Name * Description that were not tagged as a Details? A single case DU also accomplishes this. type Details = Details of Name * Description In my current understanding the record exists because you intend to give it a special name that represents the how the domain talks about the different components, in a way that the order does not matter. So if all your record titles are existing type names, a single case DU or a tuple is fine. There is already a issue in suggestions for reducing duplicate naming for single case DU. Which if memory serves is still being deliberated. //single case DU redundancy example
type DU = DU of int |
Oh, that looks nice. Do you see any substantial differences here? I mean, why are record types used at all instead of saying maps, unions and tuple types? |
Because they have element names, yes, names that are known at compile time! |
I don't like the fact there are no top level annotations, its always recommended to add annotation to top level function to make the code clear, this should apply to top level types defined like records etc. Having everything inferred could be quite troublesome. |
@Happypig375 I see. @7sharp9 It is different here, in my opinion. Top level functions can have all kinds of types. This proposal here makes always clear what type it is. I could add that the sense of this idea is to develop helpful habits and to introduce new users of the language to a certain mindset. That is the meaning and intention here, as opposed to saving a couple of keystrokes. It is about sane defaults. |
Implicit types of record elements
I suggest inferring types in record types as such:
In this example, Name has the type Name and Description the type Description on the surface.
The existing way is obviously so:
Pros and Cons
This obviously increases the type safety, reduces the clutter and makes our code distinctive.
Using primitive types on the user level contradicts the sense of type checking.
I can see the chance to increase clarity in our compiler messages.
It is also very declarative and clean to read in our type signatures.
I currently see zero serious issues here, besides of adoption and the actual integration itself.
Optional optimisation:
In order to keep up with the performance, we can use inferred primitive types in the actual compilation step and show the inferred custom type just to the user and the type checker.
So you can illusionary cover such primitive types with the here mentioned custom type.
It is possible, that the current compiler simply does struggle to infer the type of elements in record types, so this could be the actual work to do.
Extra information
Estimated cost (XS, S, M, L, XL, XXL):
S - L
Affidavit
Please tick this by placing a cross in the box:
Please tick all that apply:
The text was updated successfully, but these errors were encountered: