On line 5, he mentions that hours_to_minutes has its return value named minutes. And I can see how time.minutes would be "named" minutes. But he's really baking a lot of tacit semantics into the + operator that aren't actually obvious...
In a Python-like language, there's a distinction between the built-in "plus" statement and a "plus" function. Since + isn't a function, I'm assuming the "return value" doesn't need its own name. However, he's clearly under the impression that the expression "inherits" the names of its arguments somehow. But exactly how is unclear.
Variables obviously have names (name(var) = var). But what are the names of literals? Presumably it's kind of just "no name" (the _ wildcard he mentions?), so no errors arise from something like
any_way_you_want_it = "that's the way" + " " + "you need it"
Or does he propose to use names for literals to accomplish strong-typing? Like the name of any string is str and the name of any integer is int so that 10 + "1" would somehow be an error? If that were the case, wouldn't you be forced to use the name str instead of the above any_way_you_want_it? As far as I can tell, strong-typing would be relegated to something outside of the strong-naming system. Which might be an acceptable amount of complexity, as long as we're under no illusions about the universality of the strong-naming system.
What about mixing literals with named values in expressions? The result of an expression seems to take on the name of the named operands, as in his example where minutes needs the <- annotation:
minutes<-hours = hours * 60
These semantics are feasible. Really, the problem the compiler would need to solve would just be a simple dataflow analysis across a "names" lattice, similar to constant propagation (http://ecee.colorado.edu/~waite/Darmstadt/data.html#cprop). The lattice would have top = bad name, bottom = no name, and the "flat" elements in the middle tier (as in constant propagation) would be every possible name. Then the join operator would go
join(bottom, bottom) = bottom
join(name, bottom) = name
join(bottom, name) = name
join(name, name) = name
join(name1, name2) = top (where name1 != name2)
join(top, _) = top
join(_, top) = top
OK, so this makes sense. But it's overly restrictive. In particular, I don't know that join(name1, name2) = top is right:
hello = "hi, "
world = "planet"
hello_world = hello + world # error, since join(hello, world) = top
# and yet...
hello_world = "hi, " + "planet" # not an error: join(bottom, bottom) = bottom
hello and world individually did nothing to warrant their names: they've just been assigned to literals. But they are named (right?), and now hello + world is improperly-named, even though doing constant propagation would resolve the problem.
Could we coerce the name of hello_world to allow the mixing of hello and world? The <- notation seems unsuitable, because it only allows the renaming of a single name, as in the minutes<-hours example. Unless maybe you use a wildcard to switch off...well, really the whole strong-naming system, I guess:
hello_world<-_ = hello + world
This example isn't entirely silly, either. I've had plenty of times in code where I name intermediate values instead of just altering the same variable repeatedly. E.g., total_amount = amount_saved + amount_deleted.
At any rate, traditional copy propagation certainly becomes a nightmare. Of course, this is probably deemed a worthwhile trade-off in such a strong-naming system.
On another note, I think there's an error of omission. While he addresses the input parameters whose names you don't care about, what about functions that have no meaningful name for their return values? I think it would be appropriate to have something like
def _:reciprocal(_:n):
return 1.0 / n
(Unless you want to use a Pascal-like system where function return values are named the same thing as the function.)
It's weird though, because it further increases the distance between syntactic expressions and user-defined functions. If + were a function, it'd be one of those declared with a _ return name, because there's no meaningful way to predict the names of what you'll be adding. You'd have something like
def _:+(_:n, _:m):
while n > 0:
n = n - 1
m = succ(m) # primitive "plus one" function...again, a _ return name
return m
But if this were the case, you'd no longer have the "name propagation" in +-expressions, so his example
You're back to square one. So this strong-naming system encourages the sort of function/statement separation Python gets criticized for!
I dunno. I'm thinking the strong-naming system happens to work well for his example just because his example has variables named after units. I'm skeptical about how well the system would scale to other code bases. Good names are important, yes, but building a type system around them? Plus, it kind of puts names on a pedestal, which I think encourages mutation, which is runs somewhat counter to my normal mode of thinking.
You're absolutely right in that I've glossed over quite a few details, and clearly I've screwed up in some ways that were immediately obvious to you :P
I guess the first thing I'd have to introduce for the whole issue of basic operations destroying the entire system is to introduce something like:
def _foo:+(_foo:n, _foo:m):
...
i.e. that "underscore something" is a meta-name.
Then we have:
hello = "hi, "
world = "planet"
hello_world = hello + world # error, since join(hello, world) = top
# and yet...
hello_world = "hi, " + "planet" # not an error: join(bottom, bottom) = bottom
yes, this is exactly the point. The idea is that if you're giving it a name, that actually means something pretty significant, much more than just a way to access some specific bit of RAM with a bunch of ascii characters as reference.
I don't know how it'd scale but I imagine that many users of such a theoretical system would find it really annoying to write in the beginning without good tooling (kind of like Java with all those getters and setters :P). I do believe though that this is also the case in Objective-C and that in ObjC this tradeoff is appreciated later when maintaining big code bases. Maybe my rather draconian idea is unfeasible because of this reason alone!
I would like to have the short forms from my examples in ObjC though.. writing "x:x y:y width:width height:height" is actually something that happens quite a bit ^_-
I'm honored! (And I didn't even have to sign up for GitHub!) How'd you find your way over to this forum?
You're absolutely right in that I've glossed over quite a few details, and clearly I've screwed up in some ways that were immediately obvious to you :P
If it seems like I found it immediately obvious, it's only because I took a long time writing & discovering these thoughts before committing them to one big post. :)
yes, this is exactly the point. The idea is that if you're giving it a name, that actually means something pretty significant, much more than just a way to access some specific bit of RAM with a bunch of ascii characters as reference.
I would like to have the short forms from my examples in ObjC though.. writing "x:x y:y width:width height:height" is actually something that happens quite a bit ^_-
I do lack perspective on this because I've never used Objective-C. I mean, apparently other people already commit to more-verbose ways of having strict "name enforcement" (though I'm not sure how many people use Objective-C by choice...). It's certainly an interesting idea, and I hope to see how it develops.