HNHacker News
TopNewBestAskShowJobs

drostie

4,724 karma · joined January 19, 2012

Hi, my name is Chris Drost. I'm an application developer at U.S. Engineering Company in Kansas City, MO. If you're a talented software engineer in the KC region, hit me up -- we may be hiring.

I am @crdrost on Twitter and Facebook. I almost never check my Facebook.

I have an M.Sc. in Applied Physics from the TU Delft in the Netherlands. I am from Ithaca, NY originally.

You should listen to more Troubled Hubble.

    Don't you forget the, the image in your retina
    To foresee the future, and find a use for
    Them telling me, everything, 
      that would happen to me anyway.

    If it isn't science, it doesn't exist;
    We make our own problems and blame them on
    Birds and bees, but we ain't seen nothin' yet:
    and don't you ever forget it.
Also, more Glaze.

    Every night I wake up,
    Thinking there's something missing in my heart:
        a canvas for the art.
    Every day I hear the sound of whirring fans,
    But nothing more:
        another scene yet to be scored.
    
    You're the empress, I'm the king:
        together we will rule this world
    You'll paint the scene, I'll write the melody:
        we will rule this world.
Maybe also some '90s stuff.

    And of course you can't become, 
    If you only say what you would have done:
    So I missed a million miles of fun.
Email me by my HN username @zoho.com or my twitter handle @gmail.com.
submissionscomments
drostie··on More awful IoT stuff
It depends.

If it has "no clear copyright owner" because it comes from an entire community and has a ton of copyright holders for little bits of the code here and there, then it means that every copyright holder can sue for breach of copyright.

If on the other hand it has "no clear copyright owner" because it was written by one person under a pseudonym and that holder is almost surely never going to come forward to prove that they are the person intended... then it's de facto public domain even if that's not its status de jure. If you really want to push it in court, you can say that you met the person in-person at a conference and that they verbally told you to do anything you want with it: unless they stand up to say "that's a total lie!" there's no way of proving, because just because some code C has been licensed to a million people under the GPL, it doesn't mean it hasn't been licensed to one other person under the WTFPL.

As for how this could happen... It could be that everything suggests that they never want to come back, like the apparent disappearance from Earth of Satoshi Nakamoto; it could also be something with more stake, for example if Ross Ulbricht had gotten out of the Silk Road business and GPLed his code as the Dread Pirate Roberts before he was caught; in that hypothetical universe, nobody would want to prove that they were the DPR until some statute of limitations for running the black market had passed.

drostie··on Can Netflix Survive in the New World It Created?
TL;DR: Netflix changed everything when it switched from mail-order DVDs to streaming-video. Now, people find themselves (a) not watching commercials and (b) watching shows back-to-back. This has been lucrative for the company in terms of raw revenue, but not in terms of profits after costs: though the article is pretty murky on this (one part says that they made millions in profits; another says that they have a consistent cash-flow problem involving borrowing and more borrowing).

Netflix has several concerns up ahead which might make it difficult to survive operating at a constant loss. One is that Hulu and Amazon are encroaching on its markets, and competition tends to drive down profits. Another is that they don't own the vast majority of their content, which puts them at the mercy of the pricing of their suppliers -- and being downstream from a monopoly can also drive down profits. Even worse, their suppliers still have a revenue model based on this old advertising-and-cable-packages system, which Netflix is destroying by transforming the landscape: which is a disincentive to working with them; a channel which can broadcast a breakout-popular new TV show might keep that in-house to try to keep the channel alive, rather than licensing the show to Netflix for an alternative revenue stream. At the very least this will probably keep Netflix content about a season behind whatever's on TV.

Finally, Netflix is finding itself raising its own prices, which may lead to a flurry of cancellations from people who maintain inactive subscriptions precisely because they're pretty cheap. On the flip-side, it's not clear that they can sustain continued growth in their subscriber base; maybe at this point almost everybody who wants Netflix's service is already signed up. If those numbers don't keep up, then problems arise that are not too different from the problems with "borrowing from the future" in Ponzi schemes (albeit with a solid net positive revenue, which makes it morally very different -- in Ponzi schemes this is 0 and it is being advertised as a huge number, which is a huge lie -- no similar problem here). A similar "collapse" could happen, where the growth tapers, thereby skepticism spikes, thereby costs to borrow go high, and everything rapidly turns around from apparent success to apparent failure.

There may also be problems with the corporate culture, but while most of the article is devoted to the corporate culture, no clear message is given; it is apparently a somewhat ruthless place which is probably somewhat sad but is also sold as really exciting, because the ruthlessness only keeps the people who really care about what they're doing, which apparently creates good morale. There's a lot of other details like that which don't really amount to anything significant regarding what this "new world" is that Netflix has created, or whether it will survive the above growth pains.

drostie··on John Oliver buys $15M in debt and forgives all of it on 'Last Week Tonight'
It's worth pointing out that the Last Week Tonight clips that you see online are generally the second half or so of a longer-running HBO show: you only see the main story, and it generally comes with a spectacular call-to-action at the end (it's an intentional decision about what note to end the show on).

So the ratio for the full show is less 50/50 and more 25/75, which is much closer to your sweet spot.

drostie··on 31 years later, we found the flight recorders
Responsive design is the design ideal of not having two separate sites -- one for mobile and the other for desktop -- but rather just being able to visit one site and have its CSS dynamically adjust so that it looks good in all screen sizes.
drostie··on Async and Await
Note that this is already possible in ES6 with generators and yield, if you write your own trampoline:

https://gist.github.com/drostie/66876fc880cb17c2a6763968aef1...

Of course, it's nice to get this sort of native support for trampolining, I just wanted to be the killjoy who pointed out that as of ES6 you can already write the same thing in a slightly different way, with functions that only go up to maybe 30 SLOC or so each and therefore don't kill your codebase.

drostie··on Async and Await
To expound on this connection: the basic idea is that a Promise has a `.then(fn)` method call which takes this `fn`, which returns a value. If that value is not a Promise, then it is lifted to a new promise; either way that promise is what's returned by the .then().

In Haskell, for type safety, you have to explicitly write this conversion and the function which does it has the somewhat-confusing name `return`. The `.then()` method then has the name `>>=`, pronounced "bind".

If you have these, you have a template for type-safe functional metaprogramming. Given any x, you can produce "a program which produces an x" (return), and given any function from an x to a program which produces a y, you can bind that onto a program which produces an x, to produce a program which produces a y:

    return :: x -> ProgramWhichProduces x
    (>>=) :: ProgramWhichProduces x -> (x -> ProgramWhichProduces y) -> ProgramWhichProduces y
This was huge historically because you can still metaprogram in a purely functional language, even though you cannot do things which have side-effects. So I can give you a pure primitive like `println :: String -> ProgramWhichProduces Nothing` and you can build `println("Hello, world!")` with it, and there are no side-effects in running that function on that input, so this is purely functional. In this way the act of actually running a `ProgramWhichProduces x` as an executable is deferred until the language is no longer in play: when the compiler is run, it looks for a `ProgramWhichProduces Nothing` called `main` and writes that out as the actual executable generated by the compiler.

Monad refers then to precisely this design pattern of saying "I have some trivial embedding `x -> m x` into my problem domain `m` and I have some way to combine an `m x` with an `x -> m y` to produce an `m y`, hey, that's a monad!" ... One example is list comprehensions; there is a trivial way to embed any element as a singleton list [x] and then you can always use any function x -> [y] (called a "transducer" in Clojure) to process a list [x], concatenating together all of the [y]'s you get along the way. With ES6 generators this looks like:

    function* forEach(iterator_x, ys_from_x) {
        for (let x of iterator_x) {
            yield* ys_from_x(x);
        }
    }
Hey, that's a monad. You can write a list comprehension like:

    // equivalent to Python's [f(x, y) for x of list_x for y of list_y if predicate(x, y)]

    forEach(list_x, x => forEach(list_y, y => predicate(x,y) ? [f(x, y)] : []))
In Haskell you can write all monads with a special syntax called `do`-notation; you can write the above in any of three ways:

    [f x y | x <- list_x, y <- list_y, predicate x y]
    -- which desugars to...
    do
        x <- list_x
        y <- list_y
        if predicate x y then [undefined] else []
        return (f x y)
    -- which desugars to almost exactly the JS above...
    list_x >>= \x -> 
        list_y >>= \y ->
            (if predicate x y then [undefined] else []) >>= \_ ->
                return (f x y)
So that's the general pattern of monads and do-notation in a short, sweet lesson.
drostie··on Ask HN: Are you a programmer with Parkinson's?
No problem; I'm glad it was helpful.
drostie··on Sorting algorithms visualizer
I found out that my usual way of sorting cards as a kid was a variant of quicksort.

This is to sort the cards first into black v. red, then the black cards into spades v. clubs, then the spades into high v. low, then finally sort the low ones by inspection (a kind of insertion sort I guess), sort the high ones by inspection, sort the clubs similarly, etc.

Like most quicksorts, this definitely uses O(log(n)) space as you have a deck of reds, a deck of clubs, and a deck of high spades while you're handling the low spades...

drostie··on Ask HN: Are you a programmer with Parkinson's?
One thing that you guys might be interested in: there has been some limited investigation into tandem cycling as a therapy for Parkinson's, though it's somewhat anecdotal. What research there is suggests that it's about being forced to cycle at 80-to-90 rpm at a lower resistance which still keeps your heart rate up, as opposed to cycling voluntarily at whatever pace you want while being coached to keep your heart rate at the same level: 5 people who were forced to cycle at the higher RPM showed dramatic improvements in symptoms, as opposed to 5 who set their own RPM with coaching.

Since those numbers are so low, there is right now a team at the Cleveland Clinic which recruiting for a much larger study (100 people total) that they're calling CYCLE: 40 people with PD will do forced exercise on a motorized stationary bike in the lab, 40 will do voluntary exercise on a normal stationary bike in the lab, and 20 will not be directed to exercise at all in the lab:

http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4415238/

This is right now still in the recruiting stage so it probably has a couple years before it yields results; but if your friend can't live in Cleveland for a year, you could try to find another way to keep yourself at the 80-90 RPM levels, whether tandem biking with your friend or else by jury rigging a motor or perhaps even self-discipline.

drostie··on Ask HN: Are you a programmer with Parkinson's?
"This video contains content from NBC Universal, who has blocked it in your country on copyright grounds. Sorry about that." -- in the US.
drostie··on A Sensible Fix For TSA Security Lines
Right. As I said in my comment to the original article:

'Most of this is good security advice. This is not: "certain travelers, at TSA spotters’ discretion, [should] be exempt from screening altogether." In security, that sort of middle-ground is dumb: either the barrier should exist or it should not; if it should exist we should not have a habit of whisking people through it. Perhaps you think that there should be a barrier which checks carry-on luggage for guns: your security expectation is that nobody should have a gun on the plane. Suppose a terrorist wants to violate this expectation and get on a plane with a gun. First, he's going to print fake boarding passes to test the security line. There is no gun; he's just going through security, going to the restroom on the other side, then coming back out. He wants to know who is pulled out and whisked through -- perhaps light-skinned women, perhaps pilots, perhaps people in suits. He will then make this pilgrimage in a pilot's uniform, or inviting a female terrorist as his accomplice, perhaps even dressing her as a flight attendant. Maybe he will find that sympathetic screeners always cave to a disheveled suited man with a story, "I slept in, I have a really important business meeting, my flight leaves in ten minutes, can you help me?" If he finds any reliable mechanism to be whisked through the line, it's game over. He only has one shot to bring the gun through the screening, but he has unlimited tries to figure out what will make him look respectable enough to get that free pass.'

drostie··on Visiting Chelsea Manning in prison
If you want to open these sorts of questions you'll quickly run into the fact that there are a bunch of different judicial opinions out there. I recommend the following link:

http://legacy.earlham.edu/~peters/writing/csepref.htm

It is the preface and introduction to Peter Suber's book The Case of the Speluncean Explorers: Nine New Opinions; as the introduction discusses, the original Case, posted online here:

http://people.brandeis.edu/~teuber/lawspelunk.html#fuller

...was published by Lon Fuller in 1949 and contains a statement of facts in a (fictional!) case where a bunch of explorers get trapped for many days, ultimately eat one among their number, and are tried for murder and found guilty, sentenced to be hanged: this case is the final appeal of their sentence to the Supreme Court. In general people are sympathetic to the explorers, who faced death had they not acted.

This statement-of-facts is followed by the opinions of 5 court judges, who come to very different conclusions with very different reasonings. For example the chief justice says "Well, it's my job to apply the law as correctly as I can -- not to evaluate whether hanging these people for doing what they needed to do to survive is important. They broke the law, the law says that the punishment in this case is hanging, therefore I must rule that they should be hanged. Whose job is it to make sure that these laws are good to these people? Well, that's what we have lawmakers and an executive branch for. I'm going to sentence these people to death and hope that the President commutes their sentence." In response the second opinion says, "Seriously? That's a cop-out. Obviously if our hearts are saying that these people shouldn't be hanged, then that's not justice and they shouldn't be hanged, and in this case it's because there's a deeper Law from Nature which takes priority over our lawbooks." And so forth.

Then Pete Suber's book offers 9 more opinions, and there are even a few more in some other law reviews. Gives you a large question of how we should be balancing all of these different interests.

drostie··on Visiting Chelsea Manning in prison
She systematically exfiltrated a lot of information which exposed several lies that the U.S. government was telling its citizens and the rest of the world. This information can be divided into four groups: Cablegate, Guantanamo Bay, the Iraq War, and the Afghan War.

The US government was telling U.S. citizens that the people who were stuck in Guantanamo Bay were very dangerous terrorists who would likely be responsible for the deaths of civilians if they were released, but could not be processed via normal US law for unknown reasons either. In fact they were largely being held not because of the danger they posed to society, but the intelligence that they could provide the US. Collateral damage included two British nationals and a journalist. It also made it clear that this intelligence was largely extracted via torture.

The US government had downplayed civilian deaths in Iraq, arguing that it wasn't a huge issue and that most of our killing was very reliable and well-targeted. The current estimates of civilian deaths are closer to 2:1, that's 2 civilians killed for each insurgent killed. I am not misspeaking when I say that: the current estimate is that the majority of deaths were civilian casualties. (One particular hot-button video showed two front-line reporters with cameras gunned down from a helicopter even after they'd apparently shown themselves to be not a threat; the video however lacks two pieces of context which somewhat ameliorate one's outrage: that one of the people they were with did have a rocket-propelled grenade, and that there was a lot of fighting in that region anyways: it was a preventable overstep rather than the murder that was claimed in the video description.)

The US government in Iraq indicated that they take reasonable precautions to prevent things like torture, prisoner degradation, and other abuse of force; these documents revealed that this was not quite correct: while the US government was certainly making efforts to keep its own nose clean in the wake of the Abu Ghraib scandal, it often turned its back on similar awful practices happening at the behest of both the Iraqi police and the US mercenary corporations that it contracted, allowing them to go on with little objection and little indication to the rest of the world that these things were happening. In fact it was not just tacitly accepted, in many cases it was encouraged: US forces turned over thousands of prisoners to the Iraqi police, even though they knew that this was likely to cause them to be tortured and abused. It was damning that US forces ousted Saddam Hussein nominally for being awful to his own people (after we got into the war because he was nominally pursuing nuclear and biological weapons, which it turned out he wasn't), but then bolstered a follow-up regime which was just as awful to its own people.

A related disclosure of Afghan war documents revealed as I recall both the brutality of the Taliban and also surprised us by telling us that there was a good chance our nominal ally in rooting them out (Pakistan), was colluding with the Taliban behind the scenes. It was also a scandal for Canadians, who told their people that certain Canadian soldiers had died in a firefight with the Taliban when those documents appeared to document their dying because the US had accidentally dropped a bomb on them. For the US it was mostly scandalous because we'd understood that we were winning the war in Afghanistan; the results of these documents were much more bleak.

Finally Cablegate shocked the world because it revealed what US diplomatic informants were really saying. For example, US informants revealed a close connection between Putin and Berlusconi, and that Al Qaeda was getting its funding from Saudi Arabia, and that Qatar was a virtual safe haven for terrorists, and that Bashar al-Assad was systematically telling the US with his right hand that he wasn't arming Hezbollah while arming them with his left, and that the Yemeni government was systematically claiming responsibility for US counterterrorist operations in Yemen so that we wouldn't know that that was a front in the War on Terror. Other things emerged, e.g. the US was trading visits by President Obama for other people taking Guantanamo Bay prisoners off our hands; and King Abdullah of Saudi Arabia was embarrassed because he had privately called the then-heads of Iraq and Pakistan awful people; there were some other embarrassments in Cablegate too.

For her disclosure of these things, Chelsea (who at the time was nominally cisgendered and named Bradley) was detained and treated indecently to widespread criticism, and was finally convicted of every charge but the most severe, being sentenced for the remaining 21 crimes to 35 years in jail. This is despite the fact that her disclosure (in combination with WikiLeaks) was carefully redacted to protect vulnerable people, she won several awards around the globe for revealing the information that she did, and her disclosure ultimately caused the Arab Spring which was generally seen as a good thing for the US and democracy. It is hoped that Obama might commute her sentence at the end of his presidency, but that's a pretty slim hope; he has not publicly expressed any sympathy for her.

drostie··on Java Generics Are Turing Complete
I agree that it is not necessary in practice, no, and it is of limited theoretical use because a halting algorithm might still take ten years to do so.

However, Turing-incomplete sublanguages can be very helpful in practice, for the same reason that a `for` loop is very helpful even though it is not essential and we can solve all of our problems with `goto` which is ultimately what our compilers generate anyway. Why do most people prefer `for` to `goto`? because with the former, you pick up a writer's discipline which makes it harder to create infinite loops and to accidentally miss parts of the data structure.

Well, in any Turing-incomplete subset, it is impossible to make an infinite loop and usually very hard to miss parts of the data structure (since there is usually only a few ways to enter that data structure that the computer can prove won't halt) -- and, if you want, you can build tooling which checks your algorithms to guarantee these things. It guarantees that even any recursive handling you do with the data structure is not going to be infinite.

With that said, there are some examples of perfectly valid algorithms which don't nicely support that sort of structural recursion, e.g. if you can "jump down" to a much smaller (and sometimes hardly-related) data structure rather than incrementally descending into it. A great example of this is the GCD algorithm,

    function gcd(x, y) {
        if (y < x) {
            return gcd(y, x);
        }
        return x == 0 ? y : gcd(y % x, x);
    }
We can easily prove that this halts for positive integers by simply looking at the second parameter; each recursive call makes it strictly closer to 0, which means that it must hit 0 and stop recursing. But a Turing-incomplete subset would probably balk at both of the recursive calls, if, say, we tried to encode integers as lists of booleans to get a "binary"-ish representation: it will say "hey, you can't recurse on gcd(y, x) or gcd(y % x, x), because you're supposed to be going in gcd(x, y) from x to a substructure of x, but y and y % x are not substructures of x!"

You might be able to do it by encoding everything as a list of nulls and then first invoking some sort of decrement-increment step to swap x and y, then doing something similar for the modulus, but it means that your runtime might be something like min(x, y) * max(x, y) or something -- ugly.

drostie··on How I designed a practical electric plane for a NASA competition
Right, the first thing to always remember when you're doing a hybrid is that you're stating up-front, "I want to have two engines in this machine -- that's a weight and space cost that I want to pay up-front." As mentioned by the article, with airplanes when you increase weight everything has to get bigger to accommodate it. That doesn't make it impossible, but does make it a bit tricky.

Another interesting question: the fuel cell design in the original post is probably not reversible, is it? It would be really cool if given 10-20 more years of work we then used variable-pitch propellers with reversible fuel cells allowing for a plane which, during descent, could regenerate most of the energy used to make its ascent. It might also make pilots less worried about limited range, because they'd know that in a pinch they could get some more energy to the battery.

drostie··on Chrome removes Backspace to go back
This can be a little dicey when AJAX was used to render the form that you're thinking about submitting; for a browser to Do It Right they may need to cache complicated stuff like (a) the entire DOM and (b) the entire state of the JS interpreter at the moment navigation began away from the page. Even if we only do this on backwards navigation and immediately trash it when we navigate forwards to a new location, it still sounds to a developer's ear like a memory leak waiting to happen. It might be mitigated if you store no more than, say, 10 pages back.
drostie··on Software Design Patterns Are Not Goals, They Are Tools
You also have an equivalent on a family of isomorphic types, namely the types

    forall z. (a -> b -> c -> ... -> z) -> ZipList z
This is gotten by taking your `list_a :: [a]` etc. and writing:

    \f -> f <$> ZipList list_a <*> ZipList list_b <*> ZipList list_c <*> ...
You can of course also use Haskell pair-stacks to do all of this, storing (a, b, c, ...) as the type (a, (b, (c, ... ())))...
drostie··on Software Design Patterns Are Not Goals, They Are Tools
I've done some examples of transposing Java code explaining various design patterns from the GoF book to Haskell, then seeing how it gets simpler when you have a couple of higher-level ideas from Haskell to work with. It could make a fun talk to present at a Java-centric conference, "today I'm going to teach you some Haskell." As a bonus, you get to see how the Java code deals with a tremendous amount of needless complexity.

For example, here's what I've written so far for the Visitor pattern:

https://gist.github.com/drostie/818c54ca5c8182143699a72da986...

drostie··on Folding Paper with a Hydraulic Press
See, that is a significant point in favor of the above.

I had figured that the most plausible explanation was that elastic energy was systematically getting stored in the folds, largely because the folds were at right angles to each other (hence each one is a "spine" which forces the sheet to "stretch" to accommodate it). A prediction of this hypothesis is that a book would not catastrophically fail until a much higher pressure, and that it might fail like other objects do, by eventually squirting outward like putty.

It's possible that a hybrid approach is needed: this failure seems like a release of stored energy; this explanation needs to account for the energy storage in the cellular matrix (probably not that complicated) and then we have a ready explanation of why there is a catastrophic failure whenever the hydrogen bonds themselves begin to rip (as all the stored energy is released at once).

drostie··on The Sigmoid Function in Logistic Regression
Right, log(p/(1-p)) is the logit function; its inverse is the logistic function.

Here's another motivation for why logits are practically useful: Bayes' Theorem!

You've probably seen Bayes' theorem in the depressing form:

    Pr(C | E) = Pr(E | C) Pr(C) / Pr(E).
Here E is evidence, C is a claim, and Pr() is the probability function mapping sets of possibilities to their probability. It says that the probability of a claim, given some evidence, turns out to be the probability of the evidence given the claim, times the prior probability of the claim, divided by the prior probability of the evidence.

If you've gotten a little further with it, you may have seen that (if you work out a lot of mathematics) you can tack on a | O to all of these probabilities, where the O stands for old-evidence about the world, so that you can make this expression recursive:

        Pr(C | E O) = Pr(E | C O) Pr(C | O) / Pr(E | O).
(In case it's not totally clear, when I juxtapose sets I intend to mean their set-intersection, which is usually analogized to a sort of "set product". So `E O` above means "both E and O": given both the old evidence O and the new evidence E.)

And if you've gotten a bit further you may have seen that in practice we often will use the fact that Pr(E | O) = Pr(E | C O) Pr(C | O) + Pr(E | !C O) Pr(!C | O), where ! is of course set-complement or the prefix "not-". This allows us to write:

                        Pr(E | C O) Pr(C | O)
    Pr(C | E O) = ----------------------------------
                  Pr(E | C O) Pr(C | O) + Pr(E | !C O) [1 − Pr(C | O)]
Same theorem, but a lot more complicated because it's a lot more real-world!

But we can actually rewrite this last expression using the odds ratio,

    Odds(A) = Pr(A) / [1 - Pr(A)]
in a much simpler form as:

                      Pr(E | C O)
    Odds(C | E O) = --------------- * Odds(C | O)
                      Pr(E | !C O)
The recursive formula therefore looks like a chain of multiplications of factors due to newer and newer evidence.

On the assumption that a bunch of pieces of evidence are independent, each factor decouples from the others and just becomes some factor W_i. The logit expression looks simply like:

    log(Odds(C | E1 E2 ... En)) = log(Odds(C)) + log(W1) + log(W2) + ... + log(Wn).
The logistic regression is therefore assuming a linear approximation: each continuous factor F_i that you're studying corresponds to some independent piece of evidence E_i which contributes some factor W_i = log(Pr(E_i | C) / Pr(E_i | !C)), and we're assuming that this logarithm-of-a-conditional-probability-ratio can then be roughly approximated as a_i + b_i F_i. We collect together log(Pr(C)) + Sum_i a_i together as some leading coefficient A and then we get our fit as N+1 variables (A, {b_i}).
drostie··on From Node.js to Go
The nice thing about the generator is that (with a nontrivial amount of boilerplate!) you can pretend that the body of the function* is a synchronous zone even though it makes several calls to asynchronous promises.

The basic idea is that you wrap the generator in a sort of trampoline that turns it into a promise. This 'trampoline' detects whenever the generator falls down to it (via some `yield` of some Promise) and then waits on that promise before resuming the generator-trampoline combo. The promise then returns its result directly into generator.next(), where it becomes the value of the `yield` expression, thus within the generator `x = yield myPromise()` creates a new Promise by calling myPromise, suspends execution until that Promise completes (by dropping down to the trampoline, which bounces us up into the myPromise().then()), and finally gets the value and assigns it to x.

You have just one scope, the scope of the function* of the generator, in which all of the results of all of your Promises live.

drostie··on From Node.js to Go
You need a wrapper to make it work, but yes, you can do this. The first thing you need is a wrapper to make a Node-style call into a Promise:

    function node(fn, ...args) {
        return new Promise((accept, reject) => {
            args.push((err, ...vals) => err? reject(err) : accept.apply(this, vals));
            fn.apply(this, args);
        });
    }
What does this do? The argument of `new Promise` is a function which takes two functions, `accept` and `reject`, as its arguments. The function will be immediately run and is meant to do some stuff, possibly asynchronously, and then eventually call the first one of these on success or the second on failure. (One thing that takes a while to get used to about Promises is that generally the asynchronous thing starts when the Promise is created, so execution is not deferred ever.) We just take the args that it was called with, and append our own usual-Node-style callback to those arguments, asking it to pipe the Node error to the reject() function or the successful values to the accept() function.

Then you need a sort of "trampoline" to convert a generator-yielding-promises into a single promise. That looks like this:

    function go(gen_maker) {
        var gen, goNext;
        gen = gen_maker();
        goNext = val => {
            var item = gen.next(val);
            return item.done? item.value : item.value.then(goNext);
        };
        return Promise.resolve(undefined).then(goNext);
    }
Now what does this do? It defines a semirecursive function goNext which will hand itself to any new promises. The goNext function just runs the generator for one step with its argument, and looks at what it got out of that generator. If the generator has completed, it says "I'm done, great, return that last value!" and if it hasn't completed it says "okay, I have a Promise here, I need to tell it that when it's done computing what it's computing, it should goNext() with that value, to hand that value and control back to the generator." Finally after defining this magical function we begin the first gen.next(undefined) run by creating a Promise which immediately resolves to undefined, and telling it to goNext() with it. Of course, `gen` is the output `GeneratorFunction` that is generated by running a function* generator-maker on some arguments.

With those two basics, you do eventually de-nest everything to just one layer of asynchronicity. You of course cannot do better than this to get truly synchronous code, but you might not need to if your last expression would have been undefined anyway.

For example, here is how you then write something which accepts two arguments and appends the first to the second, using a 10MB buffer in memory. (Yes, I know this is just `fs.createReadStream(process.argv[1]).pipe(fs.createWriteStream(process.argv[2], {flags: 'a'})`, with some messing around with the high-water mark; it's just to prove a point.)

    // In this comment, we pretend node and go are defined as above, that we have
    // checked for exactly two args on process.argv and logged some usage 
    // instructions if not, etc.
    var fs = require('fs');
    const buff_size = 10 * 1024 * 1024;
    go(function* () {
        var in_file, out_file, buff, count;
        buff = new Buffer(buff_size);
        in_file = yield node(fs.openFile, process.argv[1], 'r');
        do {
            out_file = yield node(fs.openFile, process.argv[2], 'a');
            count = yield node(fs.read, in_file, buff, 0, buff_size, null);
            yield node(fs.write, out_file, buff, 0, count);
        } while (count === buff_size);
        yield node(fs.close, in_file);
        yield node(fs.close, out_file);
    });
In terms of larger libraries that do the same, so that you don't have to roll your own `node` and `go` functions, see e.g. http://taskjs.org/ and some other similar projects.
drostie··on North Korea’s one-percenters savor life in ‘Pyonghattan’
I'm not sure whether that last paragraph is spot-on or not. But it's going to require a bit of a story to explain my thinking.

First off, I'm not sure you can classify Marx's communism as a "social order"; Marx is envisioning a systematic failure of capitalism as a cohesive institution and pontificating about what might come in its wake. Because Marx doesn't believe that change happens "naturally" but only in violent upheavals, he thought "now is the time for a new social order!" in his own context, sure, and supported a Communist Party, but his analysis in Das Kapital is much more about the failure of capitalism with the increasing mechanization of the means of production.

The point is basically that Marx wants to start every social analysis by asking, "OK, in this society, who is the slave class? And who are the people who own them?" Marx saw that industrialization was increasingly multiplying the capabilities of this slave class, and indeed supplanting them. Marx envisioned that eventually, the slave class would entirely consist of robots of one form or another, with the only humans entering being the ones who voluntarily wanted to do that sort of work -- sort of the model of the CEO who leaves his cushy job because he wants to be a farmer at heart.

It's a very techno-utopian vision of the world, and it's hard to see what the social order that lives atop this is going to be. Marx famously extrapolated that the control of the 1% over the 99% was going to persist into this technological switchover, and that the 99% was going to realize it and lead a revolution-to-end-all-revolutions, because afterwards there would be born a society where every human was guaranteed the basics of everything they need (by the robot slaves and the volunteer slaves) and could pursue whatever artistic calling suited them.

But that remarkably underspecifies the nature of the underlying state, of course. The "nation with no state or laws" is one possibility, an anarcho-communist vision, but there could also be a normal modern constitutional republic, where there would be the normal boundaries in place preventing people from gathering the resources to take over the nation. It's rather hard to say.

drostie··on Show HN: BitKeeper – Enterprise-ready version control, now open-source
In other words, in Git, a branch is just a named pointer to a particular revision. In Hg, these are called 'bookmarks' and they work exactly how you're imagining; and there is an immutable sort of bookmark that is called a 'tag' (bookmarks can be repointed; tags cannot). By creating a new head (i.e. branching) and naming that new head with a bookmark, you do exactly what `git branch` does.

Mercurial also supports an autonaming of revisions which automatically applies to all child revisions, too: these are meant to be independent lines of development with their its own head revision, and are called 'named branches'; that is what `hg branch` does. The problem that you're identifying (and that I agree is counterintuitive!) is that these names become part of the commits themselves and therefore public knowledge. Mercurial warns you when you `hg branch` that this is happening and says "did you want a bookmark?" but does not tell you, e.g., "to undo what you just did, type `hg branch default`."

drostie··on Show HN: BitKeeper – Enterprise-ready version control, now open-source
Regarding your first question: Yes, you can also `git checkout -b newbranch`, rather than stashing the changes and then stashing them into a new branch; I just tend to stash my changes whenever I see that there's updates on the parent repository. Call it a reflex.

Of course, you can also commit your changes and then `git branch`, which sounds insane (that commit is also now on the master branch!) until you remember that branches in git are just Mercurial's pointers-to-heads. This means that you can, on the master branch, just `git reset --hard HEAD~4` or so (if you want the last 4 local commits to be on the new branch and you haven't pushed any of them to the central repo), and your repository is in the state you want it in, as well. (And you'll need that last step even if you `git checkout -b`, I think.)

Regarding your second one, Mercurial's simplified model is actually really smart. You have to understand that Git complects two different things into `pull`: updating the repository in .git/ and updating the working copy from the repository. In Mercurial these are two separate operations: you update/commit between the working copy and the repository; you push/pull between two repositories. The working copies are not part of a push/pull at all. So if you push to a repository with uncommitted changes in its working copy, that's fine. The working copy isn't affected by a push/pull no matter what.

With that said, if that foreign repository has committed those changes, Hg will object to your push on the grounds that it 'creates a new head', and it will ask you to pull those commits into your copy and merge before you can push to the foreign repository. (The manpages also say that you can -f force it, but warn you that this creates Confusion in a team environment. Just to clarify: a 'head' is any revision that has no child revisions. In the directed acyclic graph that is the repository history, heads are any of the pokey bits at the end. You can always ask for a list of these with `hg heads`.)

"OK," you say, "but let's throw some updates into the mix, what happens? Does it nuke my changes?" And the answer is "no, but notice who has the agency now." Let's call our repositories' owners Alice and Bob. Alice pushes some change to Bob's repository. Nothing has changed in Bob's working folder.

Now if Alice tells Bob about the new revision, Bob can run an update, if he wants. Bob has the agency here. So when the update says, "hey, those updates conflict, I'm triggering merge resolution" (if they do indeed conflict), he's present to deal with the crisis. Git's problem was precisely "oh, we can't push to that repository because we might have to mess with the working copy without Bob's knowledge," and it's a totally unnecessary problem.

Bob can also keep committing, blithely unaware of Alice's branch, if Alice doesn't tell him about it. The repository will tell him that there are 'multiple heads' when he creates a new one by committing, so in theory he'll find out about her commits -- though if you're in a rush of course you might not notice.

Bob can keep working on his head with no problem, but can no longer push to Alice (if he was ever allowed to in the first place), because his pushes are not allowed to create new heads either. In fact he'll get a warning if he tries to push anywhere with multiple heads, because by default it will try to push all of the heads. However he can certainly push his active head to anyone who has not received Alice's branch, just by asking Hg to only push the latest commit via `hg push -r tip` -- this only sends the commits needed to understand the last commit, and as long as that doesn't create new heads Bob is good to push.

drostie··on Show HN: BitKeeper – Enterprise-ready version control, now open-source
Because the diagrams for hg are very simple, there is a really simple way to do branching that obviously works and commits a relatively forgivable sin: just `cp -a` the folder.

Now, I know that that's in essence an admission of defeat! I'm not pretending that it's anything less than that. However, this is also the easiest explanation of, and model for, branching that anyone has ever created. The explanation of branching which the nontechnical user immediately understood was, in fact, just having a couple of these repository-folders sitting side by side with different names, `current_version` and `new_feature`. It is a model of branching that is so innocent and pure and unsullied by the world that a nontechnical person got it with only a couple of questions.

Like I said, I'm actually employed at an SVN shop, where branches are other folders in the root folder and the workflow is less "push this to the testing server" and more "commit to the repository, SSH into the testing server, and then update the testing server." But that Hg model resonated with someone who doesn't know computers. To me, that was a moment of amazement.

I'm not even saying which one is better really; I like Git branches too! It's just that I'm astonished that the more confusing DVCS is winning. Most peoples' approach to Git is "I am just going to learn a couple of fundamentals and ask an expert to set up something useful for me." I would guess that most Git users don't branch much; they never learned that aspect to it. I'm really surprised that software developers aren't more the sort to really say "why am I doing this?" and to prefer systems which make it easier to answer those questions with pretty pictures.

drostie··on Show HN: BitKeeper – Enterprise-ready version control, now open-source
Ahahahahahaha. You think that works!

No. That fails with the following semi-helpful error message:

    remote: error: refusing to update checked out branch: refs/heads/master
    remote: error: By default, updating the current branch in a non-bare repository
    remote: error: is denied, because it will make the index and work tree inconsistent
    remote: error: with what you pushed, and will require 'git reset --hard' to match
    remote: error: the work tree to HEAD.
    remote: error: 
    remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
    remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
    remote: error: its current branch; however, this is not recommended unless you
    remote: error: arranged to update its work tree to match what you pushed in some
    remote: error: other way.
    remote: error: 
    remote: error: To squelch this message and still keep the default behaviour, set
    remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
Actually all of these diagrams for Git need to look substantially more complicated because you first off need to introduce repositories which have a cylinder with a cloud over them (the cloud of course is the staging area) with a sort of recycle-reduce-reuse pattern of arrows `add`, `commit`, `checkout` between these three entities, with the caveat that `checkout` is only kinda-sorta what you're looking for with this. In fact there is a cylinder-to-cylinder `pull`-type operation called `fetch`, but `git fetch; git checkout` will not actually update any files, revealing the gaping hole in this simple picture, and you'll have to type `git status` to find out that you're directed to do a `git pull` anyway, which has to be diagrammed as an arrow pointing from the remote cylinder, bouncing off the local cylinder, and then pointing at the local folder.

To get to talk about `push` you then need to introduce the SVN-style "bare repository" in the diagram, a folder-box with the cylinder now drawn large inside it, and explain that this folder exists only to contain the .git subfolder and act as an SVN-style repository. You can then draw `pull` arrows down from it and `push` arrows up to it.

Then the workflow is more SVN-style:

    git branch
    git checkout
    git commit
    git push
    ssh testing-server
    cd git-repository
    git pull
Now that almost works, except the `git branch; git checkout` flow is not the proper way to push changes in the working directory to the new branch. (The context of the conversation was stuff that was already being developed, presumably on the master branch.) That fails on the checkout with an error message like:

    error: Your local changes to the following files would be overwritten by checkout:
            foo
            bar
    Please, commit your changes or stash them before you can switch branches.
    Aborting
But, I mean, close enough. It's `git stash branch <newbranch>` and it generates an ugly error message but it does exactly what you want it to do, so you can ignore that error message and hack away.

Now, you're missing the point if you think "God, drostie is really pedantically getting on my case for missing the remote-repository-update and the git stash here! Anyone will learn that workflow eventually!"

The point was not any such thing, the point was clean diagrams when explaining the idea to a fellow developer -- in fact a diagram so clean that a nontechnical user asked about it and accidentally learned enough to get some new vocabulary about how a developer's life works, so that they could more effectively communicate what they want to the developer.

It is my contention that the git diagram, as opposed to the git workflow, is sufficiently messy that a nontechnical eye will lose curiosity and most certainly will not get the idea of "make a branch, push the branch to the shared repository, then update the testing repository, then switch to that branch, then discard that branch if things don't work out." That strikes me as too in-depth for nontechnical casual users to express.

drostie··on Show HN: BitKeeper – Enterprise-ready version control, now open-source
It's astonishing to me that Git has won out given how much easier it's been for me to explain Hg to other people than to explain Git. To this day, in our SVN workflow at my company, nontechnical people who have merely seen a Hg diagram on a whiteboard by my desk immediately grasped the idea and the lingo, and ask me questions like "hey, can you branch the code to commit those changes and push them to the testing server? This thing's really cool and we don't mind playing with the alpha version, but we might scrap it all later."
drostie··on Software Update Destroys $286M Japanese Satellite
The way I read it, the bug is something like this:

There are three components, one is a Controller which goes out into the world and calculates how we're oriented, the second is basically just a Model in the MVC sense (the IRU). The first Controller has a responsibility to update the Model with whatever it finds out.

The third component is a separate part of the IRU which is essentially both a view on the model (where am I pointed, where am I supposed to be pointed?) combined with a controller which fires some thrusters to try to point you in the right direction.

The problem was that the first component wasn't updating the model. Therefore the second component set "fireThruster1: true," but then never got any information that it was approaching the right direction -- therefore kept that variable set.

The thruster continuously fired, spinning the satellite around and around until it was ripped apart by centrifugal forces.

drostie··on Zenefits Was the Perfect Startup, Then It Self-Disrupted
Thanks for the reference! Sounds like it'd be really fun.
← PreviousPage 8 of 34Next →