Anyone have an opinion on implicit member access v...
# thinking-together
g
Anyone have an opinion on implicit member access vs explicit? Or even better, seen any concrete research or reason for one over the other? What I mean is C++, Java, C#, Swift, etc when you're in a method you can access members of the current instance implicitly. You don't have to specify
this
or
self
Copy code
class Rect {
   int width;
   int height;
   int area() {
      return width + height;  // implicit
   }
}
vs
Copy code
class Rect {
   int width;
   int height;
   int area() {
      return this.width + this.height; // explicit
   }
}
I feel like I prefer explicit over implicit. IMO it makes the code more readable since I can look at the only the line in question a little easier. In the implicit case when
area()
is defined many lines below I'd have no idea if
width
and
height
are variables in a more outer scope, global variables, or member variables. In the explicit case I immediately know they are member variables. That seems like a win. It also means one less argument over naming conventions. Microsoft use to use
m_variable
. Google uses
variable_
. That would all disappear if the language required explicitness like python and JavaScript for example. What are some negatives? I can see one being typing but if for all other reasons explicit is better than implicit then some language could just shorten the syntax.
_.height
for example.
d
I believe implicit access is better, because I think singletons and global variables are rarely the best design decision. It's a pit of success thing: in general, the "likely right" way of doing things should be easier than the "likely wrong" way. If you decide that globals can be accessed unqualified but member variables must be qualified with "this.", you're encouraging global variables. Plus, I hate writing
this.
a lot, so you'll catch me writing
let foo = this.foo
in JS which just adds noise. Instead I'd propose that globals should be specially qualified instead. Ruby's convention of qualifying both
$globals
and
@members
is good too. And it doesn't necessarily have to be the programmer doing the qualification - the editor could colorize or add a sigil to indicate the scope instead.
i
I prefer explicit (and my first love of 17 years is C++). It means that the model of the language is simpler in my head and thus I need to load less context in my head; this means I have more working mind-space for conceptual things over syntactic/technical details. It's not just globals but also function arguments and scope variables one needs to care about. With explicit
this.
one doesn't need to think. Code is written less often than it is read, and I believe this applies even when I'm working on the codebase alone. I'm not a poor typist.
👍 1
d
In JavaScript, I avoid using this, new, and class as much as possible, because there are issues with them. Instead I use function-constructors, and the "members" are either the function arguments, or vars declared inside of it. These are accessed directly (without this. ) because they are captured in the closure.

https://www.youtube.com/embed/DePE0ffiMf4

👍 1
w
When I first started, I worried about these design details quite a bit, now I don't so much. Just so used to flipping. Sometimes explicit gets tedious. Sometimes implicit gets ambiguous.
👍 1
i
Dan Cook, I was actually just thinking about how exactly closures are an argument in favor of explicit member access (in the cases where one actually does choose to use 'this' and related OO patterns, that is). Closures make the, uhh.. 'identity' of identifiers even more pronounced: they are no longer just handy convenience aliases or transient temporary variables, but a central building block of the closure semantics, if you will. This in turn means that it's even more important that the semantics of the identifiers themselves be both as simple and as explicit as possible. And I feel like the implicit 'this' actually adds complexity here instead of reducing it. It extends the two categories of unqualified identifiers (the lexical local/param and ideally-lexical global scopes) with a third one: member access (which might not even be lexical if the language has dynamic typing). In order to reduce the complexity back to two categories in ones mind one has to both 1. imagine the implicit lexical 'this.' there and 2. be convinced that the implicit lexical this in the language is actually semantically identical to the other lexical constructs, which it might not be. Having the 'this.' be explicit in the first place removes both of these extra burdens: "'this' is just a function parameter of the surrounding 'method' and gets captured the exact same way as other parameters"
d
@Iridian - That makes perfect sense in a class-based language, i.e. where "x" is really just a shorthand for "this.x". However, JavaScript does not have actual classes, and "x" and "this.x" always mean different things. So when you choose to use "this", then you must always say "this.x". The usage of closures I described does not have any "this" at all, not even an implicit one. And it doesn't extend the categories, it reduces them: it's essentially throwing out member access altogether, and instead accessing everything directly through lexical scope -- not something else that can be confused with lexical scope, but actual lexical scope to begin with. It's no different than referring to "x" in:
Copy code
function foo(x) {
   ...
   return x;
}
That "x" is not short for "foo.x". There isn't a "this" that holds x, it's just the x in the current lexical scope. And you're not going to confuse the x in foo(123) with the x in foo(456), because both calls create a different "instance" of that scope. Closures are about holding onto values in that scope, rather than attaching them to some "this" object. For example:
Copy code
function plus(x) {
   return function(y) {
      return x + y;
   }
}

var plus2 = plus(2);
var plus4 = plus(4);

plus2(5); // result is 7
plus4(5); // result is 9
Plus takes some x, and returns a function that adds that x to some y. Each call to plus activates a new "instance" of that scope, with its own value for x, and returns a new function that uses that x. When you want to create multiple functions for one set values, you can return an object that has them as properties. This might be where it gets confusing, because it looks like member access:
Copy code
function foo(x, y) {
   function sum() { return x + y; }
   function diff() { return x - y; }
   function inc() { x = x + 1; }
   return { sum, diff, inc };
}

var a = foo(1,2);
var b = foo(3,4);
a.sum(); a.diff(); // 3, -1
b.sum(); b.diff(); // 7, -1
a.inc();
a.sum(); a.diff(); // 4, 0
b.sum(); b.diff(); // 7, -1
i
@Dan Cook just to avoid confusion, I wasn't extending or responding to your thought per se (the same way you weren't responding to the original, which had the premise "this exists, implicit or explicit?", which you subverted "I don't use this, so neither"). I was just noticing the funny coincidence about us both thinking about closures from different angles. So there's a high chance we're talking across each other. My point was this and only this: if one uses closures in conjunction with a class system, then explicit "this." avoids madness, while implicit "this." would invite madness. I was explicitly not commenting whether class system should be avoided, it's a bit orthogonal to the original question. On that topic, about JavaScript and classes, while you're naturally correct in that javascript doesn't have native classes but only runtime prototyping, I genuinely don't think that the difference to languages with native class semantics is that big. This is especially true now with ES6 which offers a default easy-mode boilerplate solution for classes, but I think this was true even on earlier iterations given the assumption that the boilerplating was done consistently. I mean, even in C++ the runtime semantics of classes are really not much more than dynamic dispatch plus a hidden zeroth 'this' argument. And javascript as natively dynamically typed language naturally has dynamic dispatch natively. So runtime behaviour is the same... And what comes to static typing etc., on whether dynamic typed language constructs compare to statically typed language constructs, well, that's a completely different discussion and outside the scope of this thread, I feel. 😉
d
Ah, you are correct! I indeed thought you were commenting about my reply, which made me think you didn't understand what I meant about closures in JavaScript. Yeah, maybe this has gotten a bit scattered for that reason :P ... But hey, now we have disambiguated between the different "scopes" we were speaking within. Maybe if we had only just put "this." before our separate comments, it would have been clear which contexts our comments were referring to 😉
👍 1
😄 1
i
But actually on that note: do you know if there are languages which actually have implicit "this" and fully-developed closures? My experience is mostly limited to C++ and JS, and C++ only has the rather constrainted and clunky lambdas. So neither counts.
Heh. Seems like Swift has both closures and implicit 'self'... except when used together: inside closures you need to use 'self' explicitly all the time.
d
What if the IDE/editor could label the variables: local/method parameter/class/global and keep them with the same syntax. I think is a win in case you want to change the code, having to rewrite less. And delegating it to a process made automatically by the computer and not by a human.
💡 1
b
I am against the expression of code bound to "special" scopes such as member variables and closures in general, so this discussion to me is "settling the point of precedency between a louse and a flea"
s
Intriguing take, @Bosmon. I do feel that the typical scopes end up with the problem of copying the same information around without adding any meaning. Could you elaborate on your thoughts and alternative models?
d
Java (kinda) and C# have implicit this and also lambdas/closures. C++ lambdas are clunky but I'm not sure how they aren't "fully developed".
b
Hi there @shalabh - the basic strategy is in the OAP paper that @Kartik Agaram and I were talking over in the #C5T9GPWFL thread - lets see if this link works - https://futureofcoding.slack.com/archives/C5T9GPWFL/p1544229974163900 . Instead of special scopes (objects/closures) designed to eliminate power of reference (and hence, preventing global oversight of the contents of memory), we code against a single, global, structural scope, but reserve all power of reference to the integration domain. Wherever we write anything resembling traditional code it is expressed as pure functions of its immediate arguments.
❤️ 1
i
@David Piepgrass re C++: lack of full closures means workarounds are needed. If you leave the scope you need to capture the identifiers by value (preventing sharing mutable values across several lambdas), or if you do capture local scope identifiers by reference you obviously can't use the lambda outside it. They do cover majority of the useful use cases, but I wouldn't call them fully developed because of this.
d
Okay, that's due to C++'s lack of GC so Java/C# don't have that issue. Note that Java can only capture read-only variables (i.e. it captures by value) while C# has no such limitation (but since captured variables end up on the heap, there must be limitations, as certain data types can't be stored on the heap in C#.)
👍 1
b
Shouldn't we prefer the object-oriented features that C has to offer?
e
In the Beads language we do not allow closures, as they create a weird hybrid of code + data that is not statically checkable. You will never be able to prove code correctness if you allow such weird dangling things. Closures although highly convenient for solving callbacks where you want to retain local data are quite evil, because looking at the code you cannot tell how many closures exist at any one point, thus creating great ambiguity in the code readers' minds. Closures were invented inside Actionscript 2 (which is the source material for JS, which was a direct, almost verbatim copy of AS2 at the time, including weird library quirks like the date function returning the day 1-based, but the month index 0-based. ). AS2 used closures all the time because otherwise it was super inconvenient for the AddEventListener callback functions, which inevitably need to reference some local variables telling the system where to put the data. Closures have been adopted so strongly by people like Crockford, that they program almost entirely in a meta-programming manner by generating a whole series of functions at the start of a program by customizing various functions. Makes it hard to debug unless your debugger can show the contextual variable values.
d
I'm pretty sure closures have been around before JS language family (e.g. as lambdas in Scheme); but maybe you're just taking about when & why they were introduced into JS (or its predecessors)
e
You are probably correct, as everything either goes back to LISP or Algol/FORTRAN.
b
They were in Algol-68, but not FORTRAN, which initially didn't even have a stack to support recursion.
d
Apparently a lot of things go way back:

https://youtu.be/eEBOvqMfPoI

d
Lambdas/closures are older than electronic computers themselves - see Lambda Calculus, 1936