684 karma · joined June 20, 2017
But given the sheer scale of screwups here, the number of people's affected, while I have some sympathy for stallman, ultimately I think this one really is his fault -- it's one thing to make some social gaffes now and then. It's another to actively contribute to a toxic enviornment for longer than I've been alive, to be in the public eye enough that there's no way you're not getting an earful about it from time to time, and at no point sit down and read through some of the arguments about this and consider if maybe there's something you're doing that is maybe a problem.
Again, I have some sympathy (empathy, even; I'm no stranger to being the awkward aspie guy), but there's a limit to how much you can lean on that as an excuse.
I do think angry-mob-accountability is not a very good way to deal with problems, but part of the trouble is people are going to this because the usual channels have failed them. I the way through this is to put in place systems of accountability that actually work so a civilized way of dealing with problems is even possible.
https://medium.com/@selamie/remove-richard-stallman-appendix...
This was not an isolated incident.
If it were, I'd look at his comments and write it off as his usual tendency to jump on a minor point and derail a conversation with some totally pedantic technicallity, and I do think the thing he actually said in this case has been wildly mis-interpreted and overstated in many places (the bits about him defending Epstein are clearly untrue, and even the author of the blog post that went viral and set off the shitstorm has said so, and sent corrections to the publications she'd spoken to directly).
I even agree with his statement that, from a moral standpoint 17 vs. 18 isn't really that important -- we set arbitrary cutoffs for age of consent and it's not like there's a legal determination to be made re: Minsky anyway: he's dead.
So from that mailing list thread alone, it reads like a typical autism-spectrum dude missing the social context, making a pedantic point that people read too much into and take the wrong way, and getting himself in trouble. And this is an angle that would naturally draw my own sympathies toward him.
But Stallman is on-record as saying he thinks there's such a thing as "consentual pedophila", and given that context, I think folks can be forgiven for reading into his current statements.
Ultimately though, the bigger issue isn't even about anything that happened in the past few days. RMS has been behaving inappropriately in more serious ways for decades. There are many stories out there about him harassing and propositioning students, making wildly inappropriate remarks to women, and generally making a bad situation around gender and inclusiveness in tech worse. Everyone I've talked to who has known him in a non-trivial personal capacity has corroberated this. The fact that this email is the thing prompted a blog post that happened to go viral and got people to make a fuss about it is incidental.
Even at the FSF's own conferences, he's one of the more frequent violators of the safe space policies that the organizers have put in place. I think the first year the conference had an explicit safe space/anti-harassment policy, he was the only person who violated it (in this case it took the form of a sexualized joke during his closing keynote).
I kinda have the same somewhat fearful gut reaction to these kinds of episodes as a lot of geeky guys do. There's a post[1] out there (which I think originally I found through hacker news) that does a pretty good job of analyzing where that reaction is coming from, and why the fear isn't totally illegitimate, but the idea that this is just a mob picking on some misunderstood misfit is just not what's happening here. Folks have been lienient to the point of negligience with him up until now.
[1]: https://medium.com/@maradydd/when-nerds-collide-31895b01e68c
Climate change denial in particular isn't a product of outsiders seriously questioning the scientific consensus on any grounds; it's a manifactured controversy, founded on outright lies by companies that stand to benefit if we don't do anything about it.
The reasons for anti-vaccine ideas are more complex, but they aren't the product of a serious engagement with the other side of the argument.
* The US is still 70+% white: https://en.wikipedia.org/wiki/Race_and_ethnicity_in_the_Unit...
* The number of undocumented immigrants is much lower than that: https://www.nytimes.com/2018/11/27/us/illegal-immigrants-pop...
https://sfconservancy.org/projects/current/
Half of their staff has worked for the FSF in the past. More than one of them has given a keynote at the FSF's anual conference. These folks are tightly integrated into the bits of the free software movement most closely associated with RMS; the fact that they've come out and said this is a big deal.
1. yeah, probably.
2. There's a comment elsewhere in the thread to this effect, but short-term logging for the usual purposes of managing stability/security of a system almost certainly qualifies as legitimate interest. Don't keep the logs indefinitely, but I figure nginx's defaults with a week's retention period is quite reasonable.
The relevant authorities also have a track record of giving people warnings and time to fix things, so especially for something so trivial, I'd basically just make a good faith effort and not stress about it.
Bugs happen, and users should have some tools for that situation, but if a library is repeatedly releasing one broken version after another, you probably just shouldn't use it.
I don't appreciate the accusation. At this point your own position is baffling to me as well, but I'm not going to make attacks; if it comes to that better to just walk away (I did this for a few days, because I was getting frustrated).
> Well, no. If you're claiming that the action of saying "I explicitly do not satisfy this interface" is, in a sense, a way of satisfying an interface
What I'm saying is that the built-in 'object' takes just this approach -- so if it does count as a way of satisfying an interface then (in both Python and JavaScript) every value satisfies every interface. If it doesn't, then user defined classes doing it isn't different from classes that ship with Python. I agree it's kindof weird to call that satisfying an interface; my claim is only that the explicit opting-out that `object` doesn't isn't any different than explicit opting-out in user code.
> The difference between overriding a method and overriding valueOf is that with a method, I control which specific interfaces I implement. With valueOf, its all or nothing. I cannot pick and choose which interfaces to implement. I've said this or a variant of it in nearly every post so far, and you've yet to respond to this point.
I similarly feel like I've been repeating myself with my response, which is not being heard:
This only matters if you're willing to treat the functions with special syntax (, / etc) as privileged. Otherwise `valueOf` is just another interface (representing coercion), and while it is definitely more poorly designed than the Python approach of having separate interfaces for each of ``, `/` etc, this only has any implications for a property of the language if you're willing to treat those interfaces (both `valueOf`/coercion and ``/multiplication, '+'/addition etc) as special. Some arbitrary function I write (in either language) doesn't (necessarily) use these interfaces. There are a fixed set of functions, imported by default, with special syntax, that use these interfaces (and of course code that calls those functions).
I think it absolutely is* defensible to claim the fact that these operations have specialized syntax makes them special in a way that's really "part of" the language, and if you take that as given, I think the rest of your argument is valid. But if you take those functions to be not really special, then behavior that only pertains to them doesn't inform questions about the language proper, just the libraries.
You've said that you agree the operator syntax is unimportant, but I'm having a hard time groking how else this is relevant.
I should also reiterate that this is a really nitty-gritty technical point I'm making; the ecosystem and libraries are the best thing about Python, so saying "it's just a library" should not be construed as "it's unimportant."
Re: Tensorflow, the resulting Tensorflow program/AST is statically typed, but it isn't a Python program, by any means. You can't put a Python while or for loop inside a Tensorflow program, call a python function, or basically do anything that's not encoded in the AST. You can use those when constructing the AST, but that's different. (Do correct me if I've misunderstood the design of Tensorflow). To use your own words
> [Tensorflow programs a]re no longer compatible with the rest of the language.
But this just isn't true of my examples where I forgo using javascript's built-in operators in favor of my own `mul`, `add` etc. functions. In the latter case, it's still JavaScript -- it can still be used with other JavaScript code in the full generality of any other functions I write.
I have some things to say about the Unit example as well, but I'm running late, so am going to stop here.
Not as a simple set of library functions that you import. You need to actually write an offline tool, because if the checking is done by library code then it's already too late -- the program is already running. Contrast the implementation above which is just a function, and still composes with the rest of the language without any assistance, and doesn't do anything that's terribly out of the ordinary for a function to do. Static typing is a language property, strong typing is a library property.
> As soon as you write you're own object, you've created a new language with similar syntax but distinct semantics
This seems like a uselessly broad criterion for what constitutes a language. If you're just using the same everyday mechanisms you do writing any program, I don't think you can claim you're using a different language, without diluting the meaning of the term beyond all utility.
> I'm going to challenge you to give an example, because I don't think you'll be able to.
The trivial example is keeping units straight. Merely having an attribute __mul__ does not not adequately capture the interface of multiplication in the presence of units. Unless I specifically write the kind of boilerplate with a bunch of checks like my JavaScript example, it will just silently do the wrong thing.
In some sense Python's object does satisfy every interface, with a default implementation of throw AttributeError/TypeError. This may seem trivial, but it is important in that overriding one of these isn't different from overriding valeOf, in order to get a different implementation for your object (one that throws).
This seems to be the crux of the disagreement:
> Except that strong vs. weak typing is quite literally a question of "what are the semantics of int in JS". You can't discuss weak vs. strong types without discussing the language's actual semantics, and not the semantics of some DSL you can defined on top of the language. They're all Turing complete.
To some extent it's a definitional issue; like I said in an earlier comment, I think you can sensibly argue that because + / * etc are privileged with special syntax they are "part of the language." If you chose to define it that way, then yes, the semantics of int, +, etc. matter. If you're willing to treat the syntactic sugar as unimportant, then you can just wrap the bad library api with a better one:
var TypeErr = {};
var AttrErr = {};
var mul = function(l, r) {
if(typeof(l) === 'number' && typeof(r) === 'number') {
// both are built-in numbers; use built-in *.
return l * r;
}
try {
if(typeof(l) !== 'object' || !('__mul__' in l)) {
throw AttrErr
}
return l.__mul__(r)
} catch(e) {
if(e !== TypeErr && e !== AttrErr) {
throw(e)
}
if(typeof(r) !== 'object' || !('__rmul__' in r)) {
throw AttrErr
}
return r.__rmul__(l)
}
}
// Javascript doesn't actually have ints, just floats, but there's a
// common trick with the bitwise operators to get them; let's abstract it out:
var int = function(n) {
return {
_value: n,
__mul__: function(r) {
return int(mul(this._value, r._value)|0)
},
__rmul__: function(l) {
return int(mul(l._value, this._value)|0)
},
}
}
console.log(mul(7, 2))
console.log(mul(int(4), int(2)))
console.log(mul(4, "hello")) // this thorws AttrErr.
It's not really any differrent (again, if you dscount the privileged syntax) than using requests instead of the mess that is urllib, or any other instance of "that API is terrible, let's use a different library."> Fine, stop confusing "strong vs. weak typing" with "runtime type checking". Strong vs. weak typing is about coercion. Runtime type checking is about type checking.
Run-time checking is a requirement to avoid coercion (assuming you also don't have static checking) -- everything is ultimately just bits, so if the check never actually occurs, it just "does something." Granted, you don't get active, willfull coercion like in Javascript, but ultimately if the implementation of int.__mul__ didn't do some kind of run-time checking, you would just get some garbage number when you called it. If you don't have any checking, you have coercion. Probably not object -> string, but quite likely object -> god-knows-what-memory-corruption. This is the nature of much of what you describe as C's "weak-typing" -- no runtime checks. The only difference between dereferencing a NULL pointer in C and doing None.foo in python is a run-time check.
> I really think that thinking in terms of interfaces is the easiest way to conceptualize this.
I agree. And my argument as to why strong-typing is not a feature of python-the-language is that it doesn't provide any declarative way for me to extend that property to my own interfaces; if I have some higher-level interface that doesn't fall right out of the existing Python libraries' interfaces, I have to do all of the same kind of work that I did in the above snippet of Javascript.
I think the concept of strong vs. weak typing that you're describing is coherent, but only (a) as a property of libraries, not languages, which is my argument, or (b) you assert that the built-in syntax is central. I think the latter is defensible.
Note that I am not saying that design choices of libraries that ship with the language don't matter, or even that they don't matter more than something used less often. People use these libraries every day, because they are there. The best thing Python has going for it is its ecosystem, and if you're e.g. evaluating it as a tool to use, splitting hairs like this over part-of-language vs. library probably doesn't make a ton of sense.
Re: the semantic difference, my point is that whatever the semantics of this multiply are, it's not really "part of the language" in any deep sense -- it is just another function. Given that, the semantics of multiply is entirely irrelevant to questions about the semantics of the language.
Re: "trying to makes strong typing another flavor of static typing" I absolutely am not. The "static" in static typing very much means "before the entire program is run" not just a vague "early."
Yeah, fwiw I do think that the distinction being made with javascript vs. python is meaningful, it just isn't really about the language per se, but rather the design of "built-in" functions, which often doen't need to be built in as far as their semantics are concerned.
In my comment to the sibling, reiterate the reference to julia/racket -- you could have a language feature that does dynamic checks at the call site. IIRC, Haskell's GHC has a flag to basically compile down any time errors to exceptions (I don't know that anybody uses it though).
The salient point about the example I provided was that it's implemented in Python -- I appreciate the correction regarding the actual semantics, and I agree that's a somewhat better design, but I also think the difference is irrelevant to the point I as making: that these operators aren't magic beyond their syntax.
> The difference is that strong typing makes the error happen as soon as you misuse an object (but not before, because that's not possible). Weak typing delays that as long as possible.
Sure, but my complaint is that if the contract for a function says it only accepts a Session, then passing it an Element is in and of itself an attempt to use an Element like a Session. This is the key point -- Python has absolutely no knowledge or understanding of the intended type of the function's arguments, and so as you say, it is impossible for it to know to signal an error here. For strong typing to be a language feature there would have to be some way of capturing that sort of thing. Instead, we have specific methods like int.__mul__ etc. in classes that ship with python that check their arguments and raise exceptions. That's good library design, but it's not a property of the language itself.
Note that catching the error at the call site is not as ambitious as catching it at compile time -- see the references I made in earlier comments to racket and julia, which can do some level of dynamic checking for user-defined functions/types.
def multiply(l, r):
if hasattr(l, '__mul__'):
return l.__mul__(r)
else:
raise TypeError(...)
Besides the syntax (and, again, the performance implications of the implementation), there really is nothing special about '+', '', '/' etc. They're just functions. And the implemenetation above is no more or less a design decision that the implementation of int.__mul__, which does the the aformentioned thing with strings. They very much could have implemented `multiply` as: def multiply(l, r):
while not isinstance(l, [dict, list, int, float, str, bytes, ...]):
l = l.value_of()
while not isinstance(r, ...):
r = r.value_of()
if type(l) is dict:
l = 'I love buggy software'
...
Thankfully the designers of the python builtins had a bit more taste than that. But, ultimately, just like any stand-alone function in a library, if you don't like its behavior, you can't just override a method on your own classes to make it operate differently on them, unless the function specifically makes use of that method. Your only real option is to call a different function.And unfortunately, the design decisions made for those operators don't extend to anything else written in the language. By default, if I define that I intend to only make sense to call on with an argument of requests.Session, and somebody (possibly me) passes it an etree.xml.ElementTree.Element, Python will happily chug along, with no way of knowing that this makes no god damn sense, until something goes wrong who knows where. And there is no declarative way for me to tell it otherwise; I have the same set of options available to me as in javascript.
I think it's at least coherent to claim that the built-in syntax makes these operators more than just functions, but... meh? It seems like a fairly trivial difference at that point. Even if you grant that these are really part of the language in a non-trivial way, it feels rather like saying that Go has generics because of the built-in slices, maps channels etc, or early Java, because of arrays. It's just not the same thing as a mechanism that actually extends to the rest of language in a meaningful way. If "strong typing" is to mean something about the language, it should it should apply to more than a handful of built-in operators. Python just doesn't have a mechanism that could be considered language-level support for any kind of typing (again, ignoring mypy and related stuff).
Re: constant time operations, I don't know of any language or system that does this currently. But a while back I was kicking around the idea with a friend and we came to a design that I'm pretty sure would work. Never got around to implementation though; so many projects, so little time.
> Object.prototype.valueOf = function() { throw "I will not be coerced!" }
> 4 + {}
Thrown: I will not be coerced!
This doesn't seem fundamentally different from overriding getattr. What you've got in Js is basically a handful of pre-defined functions with some syntactic sugar, which are bone-headedly written to go out of their way to not report problems. But this really isn't any different than higher level libraries like nodemailer, whose attempts to be smart have been the source of vulnerabilities before:https://sandstorm.io/news/2017-03-02-security-review
The impact is the same, and your options for mitigation are the same.
Also, fwiw, Python has some questionable decisions in the basic "libraries" too:
>>> 4 * '5'
'5555'The call to str isn't really a cast (which doesn't really have a counterpart in a language without static types); you're calling the constructor to the str class, which somewhere in it has a code path where it formats an int as a string (probably by way of calling the argument's __str__ method).
def __add__(self, other):
if isinstance(other, str):
return str(self) + other
...
Again, my point is that the distinction is mostly library design. >>> import subprocess
>>> subprocess.call([2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/subprocess.py", line 172, in call
return Popen(*popenargs, **kwargs).wait()
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
AttributeError: 'int' object has no attribute 'rfind'
In the case of `object() + object()`, that check is in `object.__getattribute__`My point is that the checks are a property of a definition of those (possibly built-in) classes; the language doesn't provide any facility to talk about types as such. Nothing in the language knows that the argument to subrpocess.call should be a list of strings, it just happily executes until it hits what is essentially:
>>> (2).__getattribute__('rfind')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'rfind'
And `__getattribute__` throws an exception. This is the best case scenario. Worst case it never hits something that these primitive types are aware is not ok, and it just does the wrong thing. There's no way to specify typing invariants in a way the language understands -- you just have to put in the manual check yourself. This is what I mean when I say strong types aren't part of the language.But if you add static types to the mix (say via mypy): If it were actually checking what should be the types involved in the code you wrote you'd get something like this:
error: List item 0 has incompatible type "int"; expected "Union[bytes, str, _PathLike[Any]]"
...which is what mypy reports when run on that code. Python isn't checking the type of the argument subprocess.call ever, not even at runtime. If you're lucky, eventually you hit some code that has an explicity sanity check in it and it raises an exception.There's an interesting point in the design space that you see with julia[1], and also with mechansims like racket's contracts[2], where the types aren't checked statitcally, but they are checked at runtime, unlike the example above with subprocess, where you only get the error deep in the implementation when something actually explodes. I think you could sensibly say that the "strength" is actually a property of Julia, rather than its libraries, but not in Python, the "strength" isn't part of the language per se.
The only differece with a "weakly typed" language is that those basic libraries have a much more footgun-like design -- again, not really about the language per se.
[1]: https://docs.julialang.org/en/stable/manual/types/ [2]: https://docs.racket-lang.org/reference/contracts.html
Arguably "strong dynamic typing" is a property of libraries, not languages -- e.g. int conceptually has a method like:
def __add__(self, other):
if not isinstance(other, int):
raise TypeError(...)
return _unchecked_add_ints(self, other)
It's implemented in C for efficiency, but that's basically the semantics. Critically, this doesn't magically extend to user-written libraries, so unless you actually write all of that boilerplate nothing but a handful of methods in the standard library can be claimed to be "strongly typed". I've written code with a bunch of asserts in methods like that after determining that a large percentage of bugs were type errors that were silently doing the wrong thing. Without something like mypy, Python is untyped by any reasonable definition; it provides no support for users to actually work with types in any meaningful way.