Open charlesroddie opened 4 years ago
This creates a troubling inconsistency though: let x = 1
creates a field, but let public x = 1
creates a property.
Does that make an observable difference to anything @Tarmil or is this just an implementation detail? This issue could be changed to refer to public fields instead of properties if that gives greater consistency?
@charlesroddie, I'm personally liking the current dichotomy between let
and member
, it is all clear / abiding to principle of least astonishment, at least once you've defined enough custom types with F# and are familiar with order of definitions.
Regarding the pros:
_.
altogether? I'm not sure it has been proposed but it would feel much more natural and solve the concern of unecessary noise: member U = u
The cons + my mitigation of the pros are making this suggestion a no go (as in no upvote from me).
In F# it's standard to have a linear structure where definitions depend only on previous definitions. Currently, because of the cumbersomeness of defining things and then creating members out of them, there is an incentive to use member t.SomeProperty = ...
which can refer to other properties both above and below. So there is a linear structure of lets
followed by some mutually recursive properties.
If removing _.
is implemented, which is linked in the OP and I hope does happen, then there will be more incentive to create properties member SomeProperty = ...
which cannot reference other properties. You could then ban usage of member t.
in a codebase and you would be left with let
s followed by properties which only depend on previous definitions and not on each other, making the whole structure linear. And I agree that would be a mitigation and a large improvement.
Relative to that situation, yes this suggestion would just avoid the need to write member U = u
at the end of class definitions. So it would save a few lines of code. But it would also allow to define fewer things (just U
instead of both U
and u
) and allow looking things up with a single "go to definition" instead of with two "go to definition"s.
I wanted to explore whether the language property of encouraging linear structures and concision which is present in the rest of the language could be maintained inside class definitions. I understand that this suggestion is a larger syntax change than the language would usually make.
@charlesroddie thanks for additional context, the actual core motivation wasn't clear to me, or rather, your additional comment makes it more explicit / helps with my reading of your suggestion. you'd like to enforce order of declaration to 100% of the code, while removing need to define the boiler plate members on custom types.
I think the idioms and design choice of having members being rec
are kind of set in stone and resonate with most people having used F# for a while, the confusion it brings to have let
become a property just on basis of visibility modifier is really going to be confusing, yet, another syntax choice to achieve your goal would be nice if it felt natural.
I personally see the F# enforced order of declaration as nice to have, especially with short let
syntax, but I'm not taking it as the strongest aspect leading to correctness in custom type definitions.
If this is a really important concern, working on an analyzer/lint making sure that members are no more than a single short expression or even a sole symbol would get you there, albeit with the cons of the boilerplate oneliners which you highlight, but I think it is minimal burden.
I just upvoted #626 as I believe it is helping a bit while not breaking the strong dichotomy between let
and member
nor making visibility modifier do something else than what they are intended.
@Tarmil This creates a troubling inconsistency though: let x = 1 creates a field, but let public x = 1 creates a property. @smoothdeveloper the confusion it brings to have let become a property just on basis of visibility modifier is really going to be confusing, yet, another syntax choice to achieve your goal would be nice if it felt natural.
On consideration, the implementation would be similar to member val
. let x = 1
creates a private field, whereas let public x = 1
would create a backing field and expose it as a property, similar to member val x = 1
.
Similarly let public Square(x:int) = x*x
would create a backing field of type int -> int*int
and expose it as a property.
The similarity to member val
means that this suggestion could also be implemented by extending member val
syntax:
let
and do
bindingsmember val Square(x:int) = x*x
syntaxI think let (mutable) public
is a more consistent syntax than member val (with get, set)
but am open to other opinions: should this suggestion use let public
syntax or member val
?
@charlesroddie, regarding the semantics of creating a backing field exposed by a property, it indeed makes more sense to reuse the member val
syntax.
Considering your main concern is enforced order of declaration, what about one or a mix of those alternative approaches:
rec
ed members call into others that aren't defined firstnonrec
modifier at member level, turning the logic above in an errornonrec
modifier at type level, turning the logic above in an error for all the members that aren't rec
nonrec type
, allow those to have mixed order between let
and member
so you can achieve what you want without the oneliners, but at the same time, we don't change the (great) defaults where members need to appear after all let
symbols. nonrec
might not be the right keyword.
Define properties [edit: and public fields] using
let public
An example class with current syntax:
I propose we add
let public
syntax to expose properties. The class could then be rewritten as:Pros and Cons
Pros:
1. a) The common pattern
let u = ...
followed bymember _.U = u
can be replaced by the single linelet public u = ...
. b) The cumbersome_.
unused bindings are not present. (They would also be removed if https://github.com/fsharp/fslang-suggestions/issues/626 is implemented.)Current F# class code is a tradeoff between 1. and 2. If you use more
let u = ...; member _.U = u
you get the benefits of code order at the expense of verbosity. This suggestion would remove that tradeoff.Cons:
member...
syntax.Notes
let public U = f()
would be equivalent tolet u = f()
followed bymember _.U = u
, sof()
is only evaluated once.There would be no way to replicate
member _.SayHello = printf "hello world"
using this syntax, althoughmember _.SayHello() = printf "hello world"
could be done aslet public SayHello() = ...
. Arguably this is an advantage as the fact that properties without arguments execute their bodies whenever called is a fact that can surprise beginners and catch out experienced users.Extra information
Estimated cost (XS, S, M, L, XL, XXL):
L
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply: