718 karma · joined October 4, 2015
You mean, you “own” copies of your data, but unless the person who controls the interface with that popular happy workflow that Everyone Already Knows How To Use lets you store it on their server, a lot of people who might otherwise be willing to collaborate with you will go “eh” and give up, and therefore, since so many marginal things require that to get anywhere, you can still just lose completely if you don't agree to the GitHub Terms of Service?
I imagine you didn't mean that part primarily, of course—but it's another thing to be aware of. There's a lot of potential for SaaSS interface lock-in—I think this is much worse for users with less technical inclination or less mental slack, but even in the realm of “people who are already developers who use Git”, this becomes a large friction source. And with more-social smaller-world flows, the coordination-control aspect has a stronger grip.
That said, it's also worth looking at how the “cloud” model is in a way reifying the folk model many users were already using. People thinking of their documents as being “in” (for instance) Word, and having no idea where they are in a filesystem or what that is in the first place, is already seared into the cultural memory of people who were family tech support in the 1990s. The default consumer cloud model is an easy extension and solidification of this: now it totally is “in” Google Docs rather than being a leakier folk-model weakening of accessing your your hard drive, and now Google Docs is accessible from anywhere. Everything is like you expect! No need to panic when your machine breaks, just get a new one and it's all still there!
And those upsides are real in their own way. But a number of downsides which are also real are less obvious and immediate.
If you start getting into different kinds of legit soy sauce then you'll find more subtle differences of course, and depending on what you're cooking, they may be relevant. I don't cook at home with enough precision for the difference between “primary” soy sauces to matter much, but if you start learning an actual cuisine, that's different.
(No affiliation.)
Fun extra fact: in Western alchemy, the symbols for major solar system objects ("planets", even though not all of them are planets by the modern astronomical definition) were also used to represent metals. Except HN seems to delete the symbols when I try to post them, so you'll have to look them up yourself, I guess!
Whoops! Oh, and there's that multi-year non-compete agreement… in the only thing I have experience in…
Fifteen years and several retry cycles later, someone who graduated twelve years after me now has the three solid years of full-time experience that I never got (in significant part because once you have a Résumé of Nothing, it stays that way), and just landed a job making about twice as much as I'm potentially going to be able to start making a month or two from now if I drive my context-switching ability and energy output right up to the limit.
Did I mention that getting access to healthcare to fix relevant issues was almost impossible for almost the entire time? At least I have a potential path to it now—and there's only a decade of backlog…
Don't do what I did and waste your life. You need a fucking real job. Do whatever it takes to keep it for long enough. Otherwise you lose.
(In fact I am not entirely coherent in my opinions on the matter, though—and the rule of equal and opposite advice applies…)
1. $5 and tail risk. Even if your financial hardship isn't that bad, if it's still making things more unpredictable, that could be $5 plus a cascade of overdraft fees one day. If things are worse, of course, it could also be your last meal for the month, or your last chance to not go homeless. Better get rid of any expense you can.
2. $5 and executive function. The payment card you were using expired. Are you going to remember to update the info when you're in constant low-grade panic or depression?
3. Relatedly to (2), $5 if you can get to that point in the first place. Maybe you had your site hosted on something more expensive, for… whatever reason. Now you have to remember how to transfer all the files and hope everything still works, and so on.
4. Kind of like (3), $5 except for the more-expensive domain name that seemed like a good idea at the time. Oops. Where are you going to move to? Will anyone be able to find it afterwards anyway?
5. $5 and relationship overhead. Which of these five-dollar hosters are trustworthy? Will they stay that way?
There's probably more.
If we care about preserving the independent Web, these sorts of problems definitely present themselves over the long run. I'm theoretically an affluent-by-many-standards technologist, but I've had issues over time that resulted in my previous sites going splat and just… couldn't really pull together what it would take to revive them, and wasn't that motivated. Decentralization of maintenance-energy overall while having individual sites relying on one or a few people has real inefficiencies that way if permanency is valuable.
Fifteen years after escaping, I still haven't published anything real, and I can see the pattern. I saw the pattern ten years ago, even, but deep conditioning is really hard to break. Only being able to finish things for other people is kind of a disaster, especially when you wind up in the “need experience to gain experience” trap.
¹ Who, again without too much detail, was Asian. ² Which never happened. She never put any actual energy into it, I had no framework for approaching it as a child, and I'm not sure the surrounding environment contained people who were willing to do that sort of thing anyway.
¹ For instance, I believe CL forbids lexically binding symbols from the CL package in ways that might conflict with the original definitions. But if you want to find uses of a particular library call from some other non-special package in a sound way, then you have to assume someone might flet that name, due to the way locals work. The underlying codewalker issue is roughly dual to the functioning of non-hygienic macros.
A compatibility hack for some applications reliant on badly-done select() might be to set RLIMIT_NOFILE to FD_SETSIZE.
(defclass tracer () ((actual :initarg :actual)))
(defun make-tracer (object)
(make-instance 'tracer :actual object))
;; But please don't.
(defmethod no-applicable-method :around (gf &rest args)
(if (typep (car args) 'tracer)
(let* ((tracer (car args))
(args* (cdr args))
(actual (slot-value tracer 'actual)))
(format t "Calling ~S on ~S" gf (cons actual args*))
(apply gf actual args*))
(apply #'call-next-method gf args)))
You can't do this with a real specialization on no-applicable-method, incidentally, because the first arg isn't special enough, it's just folded into the &rest. And that, I'm pretty sure, means this doesn't coexist with other uses of no-applicable-method properly… and you still can't do on-the-fly method names that aren't attached to a generic, and so on, but this does sort of account for the object forwarder case (and in fact you could extend it to allow tracers on more of the arguments!). It does, I expect, remain extremely unidiomatic by comparison.The bifurcation-chaos that intuitively seems to surround network effects makes for an interesting fork here, actually (assuming of course that my intuition is anywhere close to correct). Both sustained overreaction and sustained underreaction can get very bad. Looking at the earlier example, earlier communication grids' network effects were more dependent on physicality, and “everything is ephemeralized as software with effectively unstoppable automatic updates” is a huge lever in both speed and dimensionality of power. So maybe the meta problem of “legislation can't operate as a control system with enough bandwidth to keep up with technology businesses wiggling the cable to get what they want in the grapple for the ‘humanity's nervous system’ role” is more important here.
Or, perhaps, human societies decide that technology level exceeding value reach is so undesirable that the innovation explosion becomes a casualty of preserving humanity's nature, because the alternative of it eating into everything faster than it can respond (which I would say is what is happening now) is worse, and then we get Dune. Or perhaps we designate sub-areas of society as experimental zones, regulate the heck out of the stable zones, and get closer to dath ilan…
class KnowItAll(object):
def __getattr__(self, attr):
return lambda: "yes, I know how to " + attr
We then have: >>> k = KnowItAll()
>>> k.reveal()
'yes, I know how to reveal'
>>> k.transfigure()
'yes, I know how to transfigure'
>>> k.make_sandwiches()
'yes, I know how to make_sandwiches'
Ruby does this with method_missing instead, which is where I'm most used to it happening (and I think it's used a lot more in Ruby than in Python owing to the language-culture's higher tolerance for magic). Smalltalk used doesNotUnderstand, IIRC. One of the key secondary results of this is that you can do things like https://paste.ee/p/tUaRP, which is a toy “Tracer” class which intercepts, prints, and forwards method calls and attribute accesses (ignoring some edge cases).If we were in CLOS, and started with:
;;; widget.lisp
(defclass widget () ((radius :initarg :radius)))
(defmethod grow ((w widget)) (incf (slot-value w 'radius)))
What I would expect for the equivalent is that, given: ;;; tracer.lisp
(declaim (ftype (function (t) t) make-tracer))
(defun make-tracer (object) ...)
;; ... further code goes here ...
Somewhere else, we can do: ;;; fiddle-with-widgets.lisp
(let* ((w (make-instance 'widget :radius 3))
(w* (make-tracer w)))
;; ???
(grow w*))
Can you add code to tracer.lisp, without specific reference to anything from widget.lisp, such that this has the effect of (grow w) but prints what it's doing? Note also that my use of slot-value above is very deliberately a ‘raw’ access. I'm here completely ignoring the “make up entirely new methods on the fly as needed” part that method_missing also gets used for, which is even more impossible in CLOS given that it would require intercepting, what, all symbol lookups…In CLOS, the class doesn't ‘own’ the method, it provides a type for dispatching on, so there's no way to do “give me some control over every generic function so long as the first arg is of ‘my’ type”. Which is a reasonable model, but means you can't do the same thing. Indeed the flip side is that in the Smalltalk-like model, generics are not reified, and methods that are specializations of the ‘same’ thing have no ‘real’ identity to them, so you can't do a type-ignoring :around method for an ‘entire generic’. (Often there will be a superclass to attach to instead, but it's considered dangerous “monkey patching” to mess around with someone else's class hierarchy like that, and in the case of more abstract interfaces there's nothing.)
Does that make sense?
I suspect it's solvable if society has at least a slightly coherent idea of what's desired though.
The way methods are ‘attached’ to classes gives you a natural form of type-directed name lookup. CL generics have the advantage that you can define your own methods on existing classes while naming the methods in your own package so they don't conflict, but also have a curse of inconvenience along the way where importing a class doesn't naturally pull in everything associated with it, and you wind up writing the class name again and again when dealing with fields of an object. with-slots et al. are poor substitutes. (In an experimental sublanguage at one point I actually had local variables with object type declarations implicitly look up the class using the MOP and symbol-macrolet every available var.slot combination within the scope as a brute hack around the most common desirable case.)
Python's short infix/prefix operators are naturally generic, since they're implemented as method calls. In CL there's the generic-cl extension, but I haven't seen it have that much uptake… in particular, any library code that isn't explicitly aware of it won't use it ‘naturally’ on foreign objects, which could be good or bad.
That shades into the very-concrete type system that CL starts out with, where any attempt at ad-hoc polymorphic interop is a disaster unless everyone already agrees on what methods to use. I can't make a thing that acts like a hash table but uses a different implementation underneath, then pass it to something that expects to be able to gethash on it. I especially seem to get bitten by this in cases where alists are the expected way of representing key-value maps: there's no way to extricate yourself from the linear search without rewriting every piece of code that touches it, there's often an implicit contract that you don't want duplicate keys but it's easy to violate by accident and create bad behavior down the line, and so on. By comparison, Java collections in particular got this very right in terms of decoupling intention from implementation, and Python does basically the same thing but with a looser set of ‘expected’ methods.
By default, Python objects have a ‘purely’ dynamic set of properties, rather than the fixed slots CLOS imputes on an object via its class. Indeed the class-level property one can set in Python to constrain this for possible performance gains is called __slots__.