type Euler* = object _x, _y, _z, _a, _b. _c: float64 method x( this: Euler): float64 = return this._x
Doesn't seem unreasonable, does it.
Best regards Paul
Nim's compiler is insensitive to underscores, such that _x and x are the exact same thing, unlike in other languages (it's also case-insensitive, with the exception of the first letter).
There's ways to do what you're trying to do there, although I'm not the best person to tell you what's the most sensible way. The following might work, and maybe it could also be done with simple templates:
# keep properties private...
type
Euler* = object
x, y, z, a, b, c: float64
# ... but expose methods like these
method `x`*(this: Euler): float64 =
this.x # btw, you can use implicit "return" in this case
method `x=`*(this: Euler, x:float64) =
this.x = x
At least in Python, an underscore at the beginning of a variable means it's private and shouldn't be accessed by client code.
At some point I was wondering the same as the OP, but I think in Nim you just use the * suffix to distinguish "public" and "private" fields:
type
MyType* = object
publicField*: int
privateField: int
A workaround, thank you.
Yet I am new to Nim (coming from C, C++, Python) and wonder, what this.x then will mean in my Euler class. Is it the pointer to the method x or is it the attribute x?
I consider this insensitivity being a severe restriction, the lang designers are pulling the users' leg here.
Paul
The comparison with a leading comma is misleading. It's not only about scope (private vs public) but about readability, when a leading underscore is used.
Paul
Yet I am new to Nim (coming from C, C++, Python) and wonder, what this.x then will mean in my Euler class. Is it the pointer to the method x or is it the attribute x?
I tried Skaruts's code in the Nim playground and it seems accessing Euler(x: 3.0).x doesn't call the method, but returns the field value.
Maybe the compiler should at least print a warning here?
On the other hand, I guess if you use eulerInstance.x from another module, it will be a call to the method because the field x is private. I find this confusing, so for clarity I'd rather name the field privateX, which probably is a somewhat cumbersome name. I'd be interested in what conventions other people would use here!
If you write eulerInstance.x() there's no ambiguity though (see https://nim-lang.org/docs/manual.html#procedures-command-invocation-syntax ). Conversely, you can pass a proc without calling it to a higher-order function like map.
Personally I almost always use brackets to make clear that I mean the call. Exceptions are echo and cases where I understand a Nim API as a DSL (domain-specific language), like for suite and test from the unittest module.
I consider this insensitivity being a severe restriction, the lang designers are pulling the users' leg here.
As someone who has programmed mainly with Python for 20 years, so far I don't have a problem with the case insensitivity. Why do you think it's a "severe restriction"?
Note that the first letter of an identifier is case-sensitive to allow distinction between a type (with initial uppercase letter by convention) and instances (with initial lowercase letter by convention).
This is simply a judgement call of the compiler authors to name the command "lowercase gcc" to avoid inflicting caps lock or shift on people, but it's a judgement call the case-sensitive file system enables rather than blocks.
When I say whence -m '*[A-Z]*'|wc in Zsh. I get about 200 commands that have mixed case. Of course, probably close to half of those are my own things. But yeppers, sensitivity indeed means that you're forced to spell things one way. :-) Some of my upper case things are wrappers around commands like Xz around xz to avoid trivially accidental recursions. (Yes, I know there are other ways to do that. It's just another example.) I came up in physics/math not CS and there adjacency implies multiplication and other dense notations lead to font switches and boldface switches and so on all to designate extra variations/bits. So, my brain may just be more tuned to use case to distinguish as opposed to being annoyed at remembering how someone else cased something.
I mean, this has never been a deal breaker for me, personally and I even made cligen one step more insensitive (dash/'-' insensitive), but I doubt it will ever "not come up" or ever not be seen as a questionable choice. Most of Nim is super flexible at little cost (or really negative cost, large gains), but this particular feature seems to "cut both ways" - it kills name convention wars by killing distinctions people like to draw. Ident convention wars are a drag, but they're also kind of a "social problem people seem to want to have". They want to say things with these case-bits, and then the language prevents them from saying it - whether mratsim's mutability or other distinctions.
For most languages I have seen, the standard library just "sets the tone" and 90% of people go with that tone/style in many "stylistic" ways beyond just casing (e.g., short local variable names, indentation, comment density, lengths of functions, modularity styles, documentation verbosity, etc.). The pickiest people are often that vocal 10% minority. I don't think we lose people for the feature, but I think it's really hard to know how many we fail to attract.
I think resuscitating the mode where case could be optionally strict and making sure the main stdlib compiles with it would go a long way to silencing such complaints. "Just opt-in if you want that with this one-liner (or compiler option flag)!" Then maybe Nim coders only have to be style insensitive when they are cooperating with other Nim coders. I'm not sure what instigated killing that mode. Were 3rd party library writers getting a lot of annoying issues filed for "can't work in strict case mode"? Or were there other interoperation problems? In any case, a questionable default seems fundamentally easier to defend to outsiders than no choice at all. It's your compiler/language (and a great one! and thanks!) and ultimately your judgement call, as always. It's a lower priority than many other things on my own list, but I have no idea how high it rises on the lists of all those Nim fails to attract.
Another possibly constructive, possibly not suggestion (that would have prevented this entire thread from happening, as just one example) is to have "more room" for convention support. For the root of this thread, we could have identifier normalization not collapse '_' at the beginning (or end), but only in the middle (maybe allow only a single '_'). We could detect CAPS_OR_UNDERSCORE and not squash that casing. This is a slipperly slope, of course, but I doubt it would slip past the above.
It would still collapse snake_case and camelCase but also expand what people could say meaningfully to the point where they might never even notice Nim did anything weird while still killing main convention wars. Admittedly, chasing "least surprise" here also creates an even higher cost of explanation of details (and the true "least surprise" is full sensitivity, IMO). I am not even sure I am an advocate of this, but I do not recall this idea ever having been explicitly discussed before, and I think of it as a logical progression from the "admit/Admit are distinct" rule.
Sorry to revamp this, but I've been away...
@mratsim
Python has no control over visibility, all fields in a class are visible by default.
The leading underscore convention is a social workaround to a missing technical feature.
In Nim this is not a problem, and even less so because it's a static language so visibility issues are resolved at compile-time and not in the middle of a multiple-hours run.
Well... visibility issues are resolved as far as access is concerned, but not as far as reading the code is. Personally I always found it quite more comfortable to read code where private functions and variables have a leading underscore (even in C/C++, etc). Sometimes it's also useful for other denotations as well.
But I'm also not sure the issue is resolved as far as naming conventions go. I personally keep fighting the language over this:
If your type has an x property, and you want a x= setter for it (when set_x() doesn't make sense), you'll have to rename x to something else, to allow proc `x=`() to exist (to be safer, as you rightly pointed out).
But what do you rename it to?
I've been experimenting with naming my hidden properties like p_x or pvt_x, etc, but... it all just makes the code more cluttered. As far as I can tell, _x (or in some cases even __x) would be the cleanest and clearest conventions... which is probably why everyone converged on it in python and lua for whatever denotations they needed. (Occam's Razor comes to mind, too.)
How do you solve this problem without making the code more cluttered?
How do you solve this problem
First, I can not remember that I myself cared for the fact if a symbol in source code I was reading or working on was local/private or public. There may exists cases when one cares, but I just can not remember a concrete case.
Second, we can use unicode for symbol names. I never did, but maybe there is a nice unicode symbol available?
And further, when a leading underscore should indicate private symbols, then the compiler had to enforce it, so that the visible appearance is always correct. And when we use leading underscore as private mark, then we can also use question mark to indicate query as sorted?() in Ruby and Chrystal to return a bool instead a sorted copy. And then we can ask for more such visible markers.
And finally we may have IDE/Editor support to differentiate between private and public symbols.
If your type has an x property, and you want a x= setter for it, you'll have to rename x to something else, to allow proc x=() to exist
You don't have to rename x. As mentioned in the manual The resolution order of dot is well defined.
This accesses the 'host' field and is not a recursive call to host= because the builtin dot access is preferred if it is available
If underscore make you feel safer, IMO it is a illusion of safety.
I'm interested in the following use case:
type
MyType* = object
x: int
...
proc x*(mt: MyType): int =
# This is also the user API for the type.
# Have some side effect, for example update a cache.
...
mt.x
var mt = MyType(x: 7)
# Is there a way to call the proc `x` instead of accessing the field `x`?
echo mt.x
For me the question is not so much about the compiler enforcing anything (I suppose we have the asterisk to declare a field "private" :) ), but as a naming convention to distinguish between the direct field access and the call to the accessor proc. As I said earlier in this thread, you can write mt.x() to denote you want the proc call, but that's rather subtle and probably error-prone as you may forget to write the brackets.
Also, it's confusing that mt.x inside the above module will access the field whereas the same mt.x in code that imports the above module will access the proc. I understand why that is, but it still may cause confusion during maintenance.
And further, when a leading underscore should indicate private symbols, then the compiler had to enforce it, so that the visible appearance is always correct.
I don't see a need for this. For comparison, NEP 1 suggests a lot of naming conventions, but none of them is enforced by the compiler. Still, I think the conventions are useful.
For clarification, I don't ask specifically for making a leading underscore significant. It just would be nice to have some "non-cumbersome" way to distinguish the two uses of x in the above example. I don't mind that much how this would be achieved, as long as it doesn't clutter the code too much. I'm open for suggestions. If we have a way that already works with the current compiler, for example a good naming convention, that would be escpecially nice. Actually I'd prefer if we did not need a compiler extension for the distinction. :-)
Maybe the issues I had were out of misuse or confusion. Or maybe I was using templates and that can cause issues? I can't remember...
But then, I would love if @mratsim could elaborate on why he advised against doing that.