Hacker News new | ask | show | jobs
by MaxBarraclough 2047 days ago
Good article. A nice tour of properties and their pros and cons.

Personally I don't like them, for the reason labelled in the article as Problem 2: Arbitrary Computation. Computation should look like computation. I like it to be perfectly clear which statements can throw, and which cannot. I prefer List.getSize() over List.size precisely because the implementation should be permitted to perform computation, and it looks wrong when the code implies otherwise. I like to know that after executing Universe myUniverse; myUniverse.answer = 42; the value of myUniverse.answer really is 42. edit Ignoring the slim possibility of overflowing the integer type of myUniverse.answer.

Properties make it harder to reason about these things. Similar madness is possible in languages that allow overloading the assignment operator, such as C++. Come to think of it, you could probably 'fake' properties in C++ this way.

I accept that properties can make for slightly tidier expressions. I also like the point that properties are a single unit, tidily united in a way that getter/setter pairs aren't. Overall though I value 'honesty', and the ability to reason clearly about code, over these things.

I can't imagine Ada ever supporting properties, for the same reasons.

3 comments

You bring up a good point that I never addressed the case where setting a property to a value and getting it could return a different value - this is something I overlooked, and I would say absolutely needs to be true. I also didn't do a good job restricting computation, selecting time complexity as a metric instead of literally anything else - there are far better ways of doing that.

There's more to properties than just syntax-sugar as I initially said (such as being a single unit), and I think I've built more of a case for why using property-based getters/setters over fields are better that working with fields directly. If the syntax sugar isn't your cup of tea but you agree with using methods over fields, I think that's the limit of my argument here.

> I also didn't do a good job restricting computation, selecting time complexity as a metric instead of literally anything else - there are far better ways of doing that.

A tricky one, saying something as tidy as no side effects clearly doesn't work; anything we do has to be a side effect, as our hands are tried regarding return values. I don't think it would be practical to come up with perfectly precise rules for what is appropriate, only guidelines. Even never throw an exception seems too strong, as you might want to do range checks. Never block would complicate logging.

> If the syntax sugar isn't your cup of tea but you agree with using methods over fields

Yes, I think that's where I'm going with this. The advantages of avoiding public fields seem clear enough, we just disagree about the merits of properties.

Doesn't Ada have a sort of arbitrary computation on assignment? I think I remember that there are rules when predicates on types are checked. (Depending on the Assertion_Policy of course) I'm not sure how that works on assignment though.
Good point, Ada's runtime range checks mean that an assignment can raise an exception. [0] Part of what SPARK Ada does is to prove the absence of such constraint errors. [1]

I don't think Ada supports arbitrary user-specified constraints, but I'm no expert.

[0] https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gnat_ugn/Run-Time-...

[1] https://docs.adacore.com/spark2014-docs/html/ug/en/usage_sce...

You are right that SPARK tries to prove the absence of these errors. In that case you don't need these checks and turn them off. If you don't they are still run.

Ada has Dynamic_Predicate(s) which "can be any Boolean expression". [0] So, I'm guessing, you can write a pointless Predicate which does not terminate in some cases or takes unreasonably long.

[0] http://www.ada-auth.org/standards/12rat/html/Rat12-2-5.html

Thanks, I wasn't aware of that feature, I think you're right.

> In the case of Dynamic_Predicate, the expression can be any Boolean expression.

Ada functions are permitted to have side-effects (although this is disallowed in SPARK), so I imagine it would be possible to make a real mess if you wanted to.

> Computation should look like computation.

If you follow standard naming practices in C#, properties are distinct from fields, which means they do "look like" computation once you're used to it.

Add null-conditional operators and null-coalescing ones, and you can get code that is overall much more readable and easy to reason about.

If you follow the member design guidelines, you will not have any public instance fields anyway: https://docs.microsoft.com/en-us/dotnet/standard/design-guid...
Good point. A language could even mandate such a style, making the ambiguity issue go away entirely.
Yes, and the parentheses of methods obviously work as a compiler-mandated "style" that make it obvious that you're running a method. I know Visual Studio warns you about properties with the wrong casing, and I'm pretty sure you can make the compiler throw errors for those as well, so you could enforce it on a project level at least, even if the language itself doesn't.

But I agree with you on the Kotlin examples in the article. Even though it's been a while since I last did Java, my gut reaction is that it looks wrong, because it looks like I'm directly accessing a field.

So you need a little bit more than just the feature itself, you need something to distinguish them from fields when you're looking at code, or you're gonna run into surprises. Whether that is casing or syntax highlighting or naming conventions, I don't care very much, as long as there's something.

Of course, lack of discipline can screw it up, but you could name the getter for x "setY()", and make it delete the entire filesystem when you call it. You can't guard against stupidity like that...

> the parentheses of methods obviously work as a compiler-mandated "style" that make it obvious that you're running a method

The ambiguity is between properties and fields, not between properties and methods. Looking at the style guide, I don't think there's any difference between how public fields and public properties are styled in .Net. [0]

> Whether that is casing or syntax highlighting or naming conventions, I don't care very much, as long as there's something.

Ideally it shouldn't rely on fancy tooling. I'd rather it be clear in the plain text of the source.

> you could name the getter for x "setY()", and make it delete the entire filesystem when you call it. You can't guard against stupidity like that

It's true, we don't yet have a language that prevents poor choice of identifiers.

[0] https://docs.microsoft.com/en-us/dotnet/standard/design-guid...