Open LPeter1997 opened 2 years ago
Don't forget another powerful feature of properties: their usefulness in describing the values of a type. This can then be used in pattern matching via property patterns, allowing very easy matching against the values of a type. If I to choose between your proposed syntaxes, I'd go for something in the F# vein, though having a this parameter at all is weird to me. Will all instance methods need to explicitly declare that parameter? Will I need to call it in that form at the use site?
@333fred About this
, the issue about records mentions everything I believe, but the short version is: Yes, all instance methods will explicitly take this
and yes, you access all instance elements through it, similarly to Rust or Python.
Thanks for the suggestion about the syntax!
What I miss in C# is an ability to pass some kind of reference to a property that allows receiver to access property's getter and setter methods and to capture that reference in it's own field.
Use case example would be writing a generic AnimationController<T>
class/struct that could animate a value of any property of type T of any object.
I wonder if we could do it with more convenient reflection. Like methodof(MyClass.SomeMethod)
or propertyof(MyClass.SomeProperty)
, similar to typeof
and nameof
. E. g.
class MyClass
{
public int Property => 5;
}
PropertyInfo pi = propertyof(MyClass.Property);
Huh, it looks like lenses pop up all around in practice as a need.
I'd say special-casing reflection for something like this is odd and would be relatively slow, might not be ideal to drive something like an animation system. Being able to extract the getter and setter as its own entity would not be so bad I believe. Our standard library could ship something like:
trait Property[T] {
func get(this): T;
func set(this, value: T);
}
And then give the ability to construct this type in a simple manner from properties.
Some of the proposed variants make this trivial, when the getters and setters are already function-like constructs explicitly, but the more hidden ones could also get some specialized syntax for this. For example, set(Prop)
could access the setter function of a property.
From the proposed syntax i like the F# way, but i am not sure if this
should be there or not
It feels redundant, but it is also consistent from the PoV of variable binding. In C# a variable could be either local or field. Here, besides those two options, there's a third one: captured variable from primary constructor. Which makes it a bit more ambiguous
I'll propose another syntax:
impl Person {
get age(this): int = ...;
set age(this, v: int) = ...;
}
This is basically what you called D-Style but with two separate keywords for clarity.
EDIT:
This can work without the this
parameter. The point was the get
and set
keywords.
We decided to not pass this as a parameter to indicate instance fields, iirc because it doesn't work for fields.
We will use a global keyword, which is just c# static.
Global is made to be more scary than simply static, and we'll provide less scary way for good use of global, like static readonly
@Kuinox Does that mean C# static class
would be translated as global class
? I'm not sure I like that.
We didn't discussed about static class
yet.
@svick This isn't a syntax translation of C#. static
is a keyword is quite meaningless. Since we have free-functions and modules (modules being the equivalent of static classes), we didn't feel the need to keep the keyword static
as a "non-instance" modifier.
In this issue I'll discuss the basics of C# properties (for completeness sake) and present what different languages went with.
What are properties?
Properties are - in essence - syntax sugar for calling a function when retrieving a member with syntax
obj.Member
, and calling another function when assigning to that member with syntaxobj.Member = value
. The former is called a getter, and the latter one is called a setter. While the code they execute can be arbitrary, it usually makes sense that they manipulate the object state in some sensible manner.For example, if a
Person
stores their birthday, anAge
property could compute the age in years from the current time and the birthday, when using the getter, and recomputing the birthday, when using the setter.It can also make sense to omit the setter (get-only properties) or the getter (set-only properties). Set-only properties are relatively rare, it's more common to omit the setter, which are sometimed called derived or computed values, and are a good tool for reducing data-dependency, while keeping the syntax natural to the "field-notation".
Important to remember: Properties are really just syntax sugar for method calls with two kinds of signatures. Even when they look like fields, they can be declared in interfaces/traits!
Properties in different languages
Below I'll go through how different languages integrated properties and discuss how we could adapt it to Fresh.
C
C# has the general syntax
The
get
block has to return a value with typeType
, and the setter receives an implicit parameter calledvalue
with typeType
. Onitting one will make the property get/set-only.Going back to the birthday example, this could be:
C# evolved and simplified a lot on the general syntax over the years. For example, one-liners can be written with
=>
, just like arrow-methods:If the property is get-only, it can be written in an even shorter form:
C# also has so-called auto-properties, where the accessed and modified field automatically generated. Examples:
The latter is too different from simply writing a public field, but it can come from an interface signature - and a fields can't. C# generally prefers auto-properties instead of public fields, at least for classes.
For some special cases - mainly UI frameworks -, C# is planning to introduce the
field
keyword, that can be used in not-auto-implemented properties, and will result in a backing field automatically being generated, accessed with thefield
keyword (proposal). This proposal simply eliminates the need to write a backing field ourselves.Personal thoughts
C# properties are nice, but a bit too "special" for what they are. The grouped and special-cased syntax is fine, but it has some pain-points:
value
parameter being implicit, attaching attributes onto it is either impossible, or requires special casing.It makes sense why they went with this, as this ensures that the property truly has a unified purpose and the two methods are sort of related (at least by the fact that there is only one documentation for the getter and setter).
Integrating something like this into the language would be possible with some minor modifications. As of right now, the difference between static and nonstatic functions is that nonstatic functions take
this
as their first parameter. We'd either still have to introduce some keyword to annotatestatic
ness, or modify the syntax slightly to include the parameters.Python
Python properties are really close to their roots. They are simply methods annotated with an attribute, marking them as properties (and the attribute is a Python decorator, that is discussed already in the metaprogramming issue):
Personal thoughts
I believe this is very simple and elegant. Properties are nothing more, than markers on methods, and since Python methods are relatively lightweight, it's not a hassle to type it out. One huge advantage of this syntax is that everything for methods automatically becomes usable for properties too, since they are not a separate language element, but rather metadata.
Integrating something like this into the language would be pretty trivial without any nontrivial syntax modifications/considerations.
D
Properties in D before version 2 are as bare-bones as they can get. They are just getter and setter methods that you can simply use as properties.
Properties after D version 2 have to be marked with an attribute:
Personal thoughts
The pre-version 2 alternative is a bit too implicit and not very clear. The second version is virtually identical to the Python one, all advantages apply. Both versions would be relatively easy to include in the language.
Delphi
Delphi separates the declaration of the property and the methods that are executed for the getter and setter. A property simply declares the getter and setter functions, which are written externally.
Personal thoughts
A bit too verbose, but neatly connect properties back to just separate functions/methods. The advantage is that the functions are usable by themselves, they can be passed around and such.
Some syntax would have to be made up, but otherwise it's pretty simply implementable.
F
Unsurprisingly, F# ML-ifies the property syntax of C#:
Personal thoughts
By now you know how I feel about ML-syntax, but this is a relatively positive surprise. The setter has an explicit value argument and they are both method-like declarations in their shape. Something like this could be easily added to the language.
Proposed syntaxes
Below I'll propose some possible syntaxes for the language without any additional thoughts (those are already expressed in the lang-specific cases).
Python-style
Functions with a different keyword (D-style)
F#-style V1
F#-style V2
(Placement of
this
changes)Delphi-style