-
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
imply record labels from expressions #653
Comments
Would it be the same way as it is in C#? |
When implementing this, please make Refactor-Rename smart enough to add the label. Example: type R = { X : int }
let p = Point(1,2) // with properties X / Y
let r : R = { p.X } if I now rename R.X to R.Z, then it should change the last line to |
Personally, I'm not a fan of this. A rule like this doesn't seem very novice-friendly and is not immediately intuitive to me. Just my two cents though. |
As a counterpoint, javascript/typescript these days makes it very common to (de)construct values in this way. It's common to build objects like
and get an object with "foo", "bar", "baz" keys whose values are the values bound to those names at that point. Consequently, deconstructing is very nice in this simple case, while still allowing for the current more customizable syntax. In short, I <3 this suggestion. |
I'm not sure how I feel about this feature. I personally prefer to be explicit with the fields. Also, @0x53A raises a very good point; Refactor-Rename would need to be aware of this. I understand the intention is to be succinct, but we can agree that isn't always a good thing. My fear is that devs will be more inclined to name their properties or fields a certain way, even if it's not correct, so that they will be able to construct records in this succinct manner. |
This seems kinda confusing to me without much gain. To a beginner it might look an awful lot like you're making an array given the syntactic similarities, when it's very different. |
I'd say this is common enough in other languages (both C# and JS has similar features). |
What should happen if you write this? { foo=x.Name; x.Title } |
@jwosty it depends on what types map to Now if we are talking about |
This breaks the expectation that a binding should access a value rather than a name. There is an unsafe periphery of F#/.Net in which these expectations do not hold (reflection: nameof, typeof...) but this should not be moved into the core of the language. |
I think this is somewhat similar to record punning in ocaml. |
@7sharp9, looking over some of the documentation on records in OCaml it does indeed look like there is direct overlap with the concept discussed here and field and label punning. It also appears that ReasonML enjoys a similar sytactic shortcut as well as perhaps PureScript, JavaScript as of ES6, and C# with anonymous types of course. It looks like some of these implementations differ based on whether they work beyond variables/fields or work on extended property get accessors like in C#. References:
|
I like this idea, but case sensitivity of names should be addressed. I think it would be weird for pre-defined records if it was case sensitive. For example, this would happen: type MyRecord =
{
Foo : string
Bar : int
}
let createMyRecord foo bar = { foo; bar }
// error FS0039: The record label 'foo' is not defined. For anonymous records, shouldn't it also construct fields with Pascal case to be consistent with naming guidelines? |
I've wanted to suggest this feature for years now. I've been using it in my own language which is syntactically similar to F#, and I always miss being able to pun when I use records in F#. I hope to see this in the language at some point. |
I'd expect this to work with struct DUs, where it is currently required to add redundant labels that you'll never use in practice. Having this for records is nice, but could arguably add to confusion, but for struct DUs at least it would remove such confusion and redundancy. |
I've done a little typescript for a project and found this kind of "punning" (if that's the right term) quite natural. I am going to remark this as approved-in-principle. We should do this. |
Yes, exact matches only |
If I'm understanding the exact match comments right, these still wouldn't work after this feature is implemented: type MyRecord = { A: string; B: int }
let f a b = { a; b } // used for validation
let f { a } = ... // destructure what I need
let f { a; b } = { b; ...} // convert record type, often to subset of original
match msg with // specific DUs have record values in every case
| MsgCase { a; b } -> ... I'd have to use the existing verbose syntax for these. Or use upper case value names against the naming guidelines. Addressing the record copying case |
Since this issue is having some action, let me plug my own language Spiral. Compared to the last time I posted here, it is finished, and you can check it out on VS Code marketplace if you want some inspiration. |
The casing thing is really unfortunate and artificially reduces the usefulness of this feature, but at the same time automatically uppercasing/lowercasing things in the language feels very wrong. In fact, there's similar weirdness in C# when it automatically gives a name to tuple items: sometimes the name will be lowercase, sometimes uppercase, depending on the expression you put there. Add this to my reasons for disliking .NET's casing conventions 🤷 |
Maybe it's just my usage pattern. I use |
Yes, I agree. That said, I'm assuming that in Fable and Python F# the use of lowercase record field names is much more common - and you see it in some .NET F# as well. So I don't think the feature is without utility. |
Ah, so this would represent an unofficial shift in the naming guidelines. At least, I would use camel case property names to have access to this feature for the use cases I mentioned. |
We can always mine the quarry of guidelines, carve the stone of records, and etch the new guidelines into it 🙂 |
@cartermp I'm into it. :) We inherited most of the guidelines from C# anyway. |
… latest AyaTri chapter and then I will get started. I've been looking forward today's session. 9am. Let's start. /// Compare F# to Typescript. In F#, the compiler places most of the types for you, but Typescript requires you to onerously annotate everything by hand, so F sharps type system is a lot less intrusive. Typescript's is more powerful, but because placing all the types is difficult what you'll see in the wild is people not bothering to put the types in. /// /// And programs with complex typing are harder to understand, not easier. In the future, there will be all kinds of languages with advanced type systems, but understanding programs written in them won't necessarily be easier those written in a language like F#. /// I'll cut these out. 9:45am. 3 minutes of ranting should be enough. 11:10am. https://www.brightball.com/articles/the-time-i-accidentally-ended-up-combating-fraud-for-a-year I'll read this later. ``` "profiles": { "Watch": { "commandName": "Executable", "executablePath": "dotnet", "workingDirectory": "$(ProjectDir)", "hotReloadEnabled": true, "hotReloadProfile" : "aspnetcore", "commandLineArgs": "watch run", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, ``` ``` "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "hotReloadEnabled": true, "hotReloadProfile": "aspnetcore", "commandLineArgs": "watch run", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "http://localhost:5037", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "hotReloadEnabled": true, "hotReloadProfile": "aspnetcore", "commandLineArgs": "watch run", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "https://localhost:7240;http://localhost:5037", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "hotReloadEnabled": true, "hotReloadProfile": "aspnetcore", "commandLineArgs": "watch run", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } ``` Let me back this up. I am not sure how hot reload could be made work with IIS, but nevermind that. 12:45pm. Let me stop here so I can take a break. Sigh, it just takes so long to get going with anything new. 1:15pm. Got a Youtube comment. /// I think transcribing from any typescript code to F# should be possible, maybe at the cost of paying for API ergonomics. Perhaps the problem is that ts2fable isn't mature? Then that's a solvable problem that isn't intrinsic to F#. F# honestly needs love/funding. (note: I haven't used F# nor Fable, but I am looking to use it) /// /// The problem with transcribing TS's types to F# is that TS has a lot more powerful type system, and a lot of what would be expressible in it, simply isn't in F#. All of type level mappings, type literals, type conditionals, nested variadic args. Moreover, F#'s (.NET's) and JS's object systems are different. You'll always be faced with incompatibilities in such a situation, and TS which was made to be a superset of JS will always have an edge when interacting with it. Currently, I am trying out Bolero & Blazor, so depending on how that turns out, it might be better to use those for web dev with F#. I need to spend some time using them before I decide whether I want to recommend it or not. I'd recommend F# in general as it is a lot easier to use (and a better language) than TS, but after experiencing Fable, but as far as webdev is concerned, I am not sure whether it would be worth the users time to use it over TS for their own projects. Maybe if you are an experienced webdev who is familiar with the libraries on the TS side, you could get use out of Fable, but for somebody coming in, definitely not. Bolero might be worth a try. /// 1:35pm. Let me resume. Lagoon by Luke Bergs & LiQWYD | https://soundcloud.com/liqwyd/ License: www.liqwydmusic.com/how-to-use Creative Commons CC BY 3.0 Essence by Luke Bergs | https://soundcloud.com/bergscloud/ Creative Commons CC BY-SA 3.0 Blessed by Luke Bergs | https://soundcloud.com/bergscloud/ Creative Commons CC BY-SA 3.0 Enjoy Yourself by Steven O'Brien | https://www.steven-obrien.net/ Creative Commons CC BY-ND 4.0 Music promoted by https://www.chosic.com/free-music/all/ https://creativecommons.org/licenses/by-nd/4.0/ 4:20pm. It is rendering. 4:40pm. https://youtu.be/HYCXCviAXYo VN Compiler. How to use Blazor components with Bolero. Introducing Blazor.Diagrams. (Pt. 1)(Restart) Here it is. Let me post it on the F# sub. 4:50pm. Do I feel like going on anymore for the day? 5:05pm. https://blazor-diagrams.zhaytam.com/customization https://www.syncfusion.com/sales/communitylicense Oh, the community license for this is free. 6:45pm. https://youtu.be/ws5-CUzY7_w Blazor Context Menu Component | Right Click Menu https://youtu.be/ws5-CUzY7_w?t=39 Lol. https://medium.com/it-dead-inside/lets-learn-blazor-mudblazor-to-the-rescue-711129babc18 /// These come in two general camps: The overpriced corporate suite I’ll not name names, but you don’t have to look far. These are companies selling razor / Blazor components in a suite of libraries you can just download and go. Don’t get me wrong. A lot of them are very professionally done (great documentation, easy to use, pretty customisable and decent-looking), it’s just the pricetag which is too much to bear. I’m an independent developer. I don’t have the kind of corporate coin they’re after for a developer seat. The underwhelming open source project This is in no way a cut on anyone who’ve put in the time and effort to build free-to-use razor / Blazor components, but honestly most developers are TERRIBLE at things like styling, documentation, and in the land of open source, bug fixing. You get what you pay for, and for a whole lot of these open source Blazor component libraries, you are definitely paying for what you’re getting. Maybe not in terms of money, but definitely in terms of your time and frustration. A lot of the time these components are so bland and clunky, you end up spending all your time customising them to fit your needs. So you either shell out huge amounts of money, or you fight with open source / free components which are not up to snuff. Again, a love / hate relationship. Like so many devs in the Blazor space, I wanted a fully-featured library of components which looked and reacted amazing in my app, and was either free or at most reasonably-priced. So I was blown away when I stumbled upon what seems to me to be the most robust, powerful and well-documented set of Blazor components out there, all open source and free to use. Introducing MudBlazor. 8:40pm. /// Thank you so much for doing this. You put into words the exact experience i've wanted, which is basically "hey how do I stick with what I know to get a frontend up". I'm curious what improvements you'd like on record syntax. /// /// I signed up for SyncGusion community license a few hours ago, and now I am looking up how to do context menus in Blazor. https://github.com/MudBlazor/MudBlazor/ This is pretty cool. > In the last couple weeks, MudBlazor has undergone a huge overhaul, including a fully-retrofited website and documentation, complete with example code and markdown for each component. There are dotnet new templates for you to get started and heaps of examples to cover your situations. Where is the component list. I can't find anything in the docs. https://mudblazor.com/components/list#api Here it is, why did I have to type this into Google? https://mudblazor.com/components/menu#custom-activator Here is the MudMenu. I can put this on any interface element. https://mudblazor.com/components/menu#placement This is really nice. This could be super useful. With MudBlazor, I wouldn't need to download a package just for the context menu. It never occured to me before, but I can just place thing thing on the outermost element and it will trigger whenever anything underneath gets clicked. 8:40pm. https://old.reddit.com/r/fsharp/comments/13osxyl/vn_compiler_how_to_use_blazor_components_with/jl6faxn/?context=3 /// > I'm curious what improvements you'd like on record syntax. Basically, what I've made [in Spiral](https://github.com/mrakgr/The-Spiral-Language#records). There is even a [F# issue](fsharp/fslang-suggestions#653) to improve the syntax. Let me summarize it a bit. Instead of writing... let f {a=a; b=b} = a+b let g (a,b) = {a=a; b=b} You can write the following (in Spiral). let f {a b} = a+b let g (a,b) = {a b} This is one of the places where even JS has better syntax than F#, and the language could be easily extended to support something like that. Spiral also has nested updates. type E = { q : int } type W = { b : E } type Q = { a : W } let r : Q = {a={b={q=0}}} let x : E = {r.a.b with q = 10} In F#, that `{r.a.b with q = 10}` would return the innermost record, but in Spiral it nests the updates and return a record of type `Q` as it was originally. And this is much more useful behavior. Also Spiral supports the update form where instead of assigning to the record, you pass a lambda to grab its argument. {r.a.b with q #=fun x => x+2 } This is also useful as it makes it easy to grab whatever the field you are accessing is. {r.a.b with q #=((+) 2) } Since it is a function you can partially apply it. {r.a with b #=fun b => {b with q #= ((+) 2)} } By going up a level you can also easily update the outer field and so on. Anyway, Haskell has crappy records, and F# leaves a lot on the table in regards to ergonomics with them. I'd be using them a lot more instead of tuples if the syntax for them was better. /// Let me close here."
I propose we allow both records and anonymous records to imply record labels from a limited range of expressions, e.g. for anonymous records:
is the same as
The same would apply for records. The label names would be implied by a
expr.Name
lookup only.It would be natural to do this at the same time as anonymous records.
The existing way of approaching this problem in F# is to write the label names explicitly.
Pros and Cons
The advantages of making this adjustment to F# are succinctness
The disadvantages of making this adjustment to F# are you have to know the rule, and it makes the selection of type a little more implicit, especially in the case of records.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Related suggestions: #207
The text was updated successfully, but these errors were encountered: