HNHacker News
TopNewBestAskShowJobs

alquemist

244 karma · joined July 1, 2020

submissionscomments
alquemist··on How Shopify Uses WebAssembly Outside of the Browser
Rarely. Over a long enough time period though, the probability of a portability event nears 1, and the cost of such event is enormous. Right now there is a credible challenge for the x86 domination in the server, laptop and desktop markets raised by ARM, via AWS A1, Apple M1 and many others. Would be foolish to bootstrap an ecosystem locked-in to the loser, and we don't know the winner / loser over a 5-10 year timeframe.
alquemist··on How Shopify Uses WebAssembly Outside of the Browser
I am not following WASM closely, but it appears to be deployed in all modern browsers: Chrome, Firefox, Safari, Edge. That counts as 'large scale production deployment', even if there aren't that many websites that take advantage of this capability (yet?). https://caniuse.com/wasm
alquemist··on How Shopify Uses WebAssembly Outside of the Browser
In a WASM + seccomp implementation, the whole WASM runtime runs inside seccomp. Breaking out of WASM leaves one running arbitrary asm inside seccomp, which has exactly the same attack surface as directly running untrusted binaries inside seccomp. The WASM layer in WASM + seccomp simply requires an additional exploit.
alquemist··on How Shopify Uses WebAssembly Outside of the Browser
That's a bit of apples and oranges. JS/WASM are runtimes executing hostile code, whereas Go apps are trusted code.
alquemist··on How Shopify Uses WebAssembly Outside of the Browser
Assuming that Intel / ARM microarch implementations are bug free, that is correct. In the real world there are no bug free implementations.

Edit. This is the strategy Chrome sandboxing uses: a hardened runtime (JS/WASM) inside a seccomp enclosure. https://chromium.googlesource.com/chromiumos/docs/+/master/s...

alquemist··on How Shopify Uses WebAssembly Outside of the Browser
* Predictable performance.

* A wide ecosystem of mature language toolchains.

* Simplicity: JS implementation contain sophisticated JITs, which are harder to prove correct compared to a simple ASM translator.

* Portability: not tied to a specific HW architecture.

alquemist··on How Shopify Uses WebAssembly Outside of the Browser
It's not an either/or. Most likely Shopify runs WASM inside an seccomp enclosure. Possibly inside a VM as well. Defense in depth.
alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Mock: Adhoc guessing of what methods called on a dependency, but sometimes even between classes in the same module, might return. Guess repeated over and over as new tests are added, sometimes tens of times or even more. For example, https://site.mockito.org/#how, "when(mockedList.get(0)).thenReturn("first"); System.out.println(mockedList.get(0)); // prints "first""

Fake: A replacement module that behaves like a production module, but with certain simplifications, for example in-process vs. using rpcs, or simply cleaning up the filesystem after usage. For example, https://github.com/tk0miya/testing.postgresql. "automatically setups a postgresql instance in a temporary directory, and destroys it after testing".

alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Agreed. Using mocks while testing vanilla in-process code is never justified.
alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Fakes are not mocks. The fake is just another module. Assuming no updates, it is written once. Mocks are written 47 times, inconsistently, while focusing primarily on other tasks. If there are updates needed, better to fix them in one place than chasing 47 test code locations with inconsistent usages.

Furthermore, these are external dependencies that can't be run in-process. If a dependency can be run in-process (aka library), there is no justification to ever mock it. I've even seen codebases that mock their own class B in order to test class A. Run the production code already. Ban mocking libraries.

alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Fake, don't mock. Write, or ask the team that provide the external dependency to write, a small piece of code that behaves like your external dependency, but in-process. You'll thank me after about the 47th time you're guessing (inconsistently, possibly incorrectly, and definitely overly verbose) how the external dependency actually works. Ban mocking libraries.
alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Fake external storage / rpc dependencies.
alquemist··on Death by 1000 layers: the perils of over-abstraction in Java (2017)
Don't mock. Test production code, maybe with faked storage.
alquemist··on Why software ends up complex
"In the beginning was the word". Language shapes reality. As software engineers, the second we accept that 'product owner' is a legitimate title, that second we lost agency to push back on poorly conceived features. Say it loud and clear: you also have a stake in the product.
alquemist··on U.S. Treasury breached by hackers backed by foreign government – sources
https://leanprover.github.io

While it is difficult to design a secure procurement chain all the way to the SiO2, we could at least design simple enough hw/sw systems for which formal verification is an economical option. And then force government entities to use formally verified systems instead of the bug ridden crap most shops, especially the sw ones, have to ship under intense deadline pressure. The market has led us into a broken local optima, no way to get out short of state level action.

alquemist··on Monads and GATs in Nightly Rust
Can not resist. The tension between 'basic feature set' and an admittedly superficial reading of the docs is very funny. The manifesto links to https://github.com/commercialhaskell/rio#readme and urges us to use the rio library to get started. Upon opening the rio link and scanning for a list of the 'basic feature set', I stumble upon the first block of quoted code. After removing 39 eoln characters in respect for the HN audience, it reads:

"Our recommended [language extensions] defaults are: AutoDeriveTypeable BangPatterns BinaryLiterals ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DoAndIfThenElse EmptyDataDecls ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude OverloadedStrings PartialTypeSignatures PatternGuards PolyKinds RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TupleSections TypeFamilies TypeSynonymInstances ViewPatterns"

39 language extensions just to get started. This screams 'incredibly complicated', even if perhaps reality is rather more mundane. Consider the 40th language extension: GradualTyping, so perhaps those that would rather write code about data than about types using a half baked and evolving type language (which taken to its logical conclusion will have to become a full fledged theorem prover in the Coq / Idris / Agda / Lean lineage anyways) could get their jobs done.

Wish you guys all the best!

alquemist··on Monads and GATs in Nightly Rust
99% of the time a loop works just fine, because there are no measurable gains to be had from parallelism. For the 1% where performance matters, it's usually a bit more involved that simply using a map or fold, and hopefully already packaged as an off-the-shelf library. To have measurable gains from parallelism one has to be very intentional in balancing communication vs computation. Think carefully designed libraries like cuDNN.
alquemist··on Monads and GATs in Nightly Rust
'FP' covers 2 meanings:

A. Pure functions + immutable data structures.

B. Expressive type systems, all the way to compile-time metaprogramming and dependently typed proofs.

Writing programs in style A. is tremendously valuable. Expending too much effort in the fine points of the type system, which invariably is simultaneously both under-expressive and over-expressive, is a complete waste of time. Some critical projects require high defect-free confidence, and for those it's legitimate to go full in formal proofs and take the 10x-100x productivity slowdown. For mere mortals, documenting the structure of the data (json) manipulated by the respective functions suffices.

alquemist··on Monads and GATs in Nightly Rust
Haha, I'm the wrong person to ask. I was fortunate enough to be in a hands on senior role, and I promoted a very light Java++ style, to be learned in a 2 hours seminar: immutable collections, case classes, pure functions, impure logging & exceptions, sparingly used interface polymorphism, recursion, filter/map/flatmap/fold. Scala: The Good Parts. To this day I have not learned the first thing about implicits or type variance, and probably there are many many many more Scala features I haven't even heard of. The biggest challenge was helping the rest of the team avoid writing inscrutable sbt plugins. The plus side, it makes little difference Scala2 vs. Scala3. The downside, some teams may scoff at such a mundane approach.
alquemist··on Monads and GATs in Nightly Rust
I used Scala2 professionally for a few years. Recently picked up Typescript, it has become a very usable Java++ language.
alquemist··on Error Handling Is Hard
RAII can be decoupled from ctor/dtor mechanism, see Python's 'with' statement. Javaish pseudocode, similar to a 'for' loop:

    // on exit the finally fn will be called with 'x' as an argument.
    with (Type x: create(args); finally (Type x) -> cleanup(x)) {
      // safely use x, throw at will.
    }
Re C++ and exceptions, there is an additional layer of wrinkles: throwing exceptions from ctors or, worse, dtors. Herb Sutter used to have a loooong list of what can go wrong in such situations back in the day. Explicit error codes happen to make it impossible to write ctors / dtors that can enter an error state by virtue of the fact that there is no way to return the error. My suspicion is that this is 50% of the reason of banning C++ exceptions in solid C++ style guides.
alquemist··on Error Handling Is Hard
Thanks. Then the 'bad' code should look something like:

    // bad
    NotifyIcon CreateNotifyIcon()
    {
      NotifyIcon icon = new NotifyIcon();
      icon.Text = "Blah blah blah";
      system.Display(icon);
      icon.Icon = new Icon(GetType(), "cool.ico");
      return icon;
    }
Which would make noticing the coding error quite a bit easier, as we are passing an obviously incompletely initialized object to a third party. As presented, the code presumably is intended to be used like so:

    NotifyIcon icon = CreateNotifyIcon();
    system.Display(icon);
which works in both versions. If 'icon.Icon = new Icon(GetType(), "cool.ico");' throws, then the NotifyIcon is never displayed.

As presented, this is hardly an argument that explicit error passing code is better at detecting bad code than exception based code. His other article, https://devblogs.microsoft.com/oldnewthing/20040422-00/?p=39..., is making the point a bit more clearly.

The point itself is half-sound. While it is true that with exception based code, it is hard to distinguish which statements may fail, the real issue is about correctly releasing resources. Assume that every statement can fail, and use some form RAII to manage resource cleanup. The exact content of RAII is hard to glean in both explicit error code and exception based code, as it depends on how the specific third party API acquires/releases resources. Though APIs can be organized to essentially force the use of RAII, even if popular but ancient APIs like POSIX file system are not designed that way.

The real language design issue is that Java/C# mechanism for running cleanup code 'try {} finally {}' a. fails to pass through a handle to the resource that needs cleanup and b. is not scoping the lifetime of the resource that needs cleanup. Furthermore, the standard API makes no effort to provide RAII constructs for correctly managing resource lifetimes. Those language ecosystems actively steer people towards writing bad code. Exceptions may be fine, but the lack of language supported RAII is definitely poor language design. For better language design, exception based Python offers 'with' mechanism and error code based Golang offers 'defer'.

https://www.python.org/dev/peps/pep-0343

https://golang.org/ref/spec#Defer_statements

alquemist··on Error Handling Is Hard
I have to agree with the author. It is extraordinarily difficult to see the difference between bad exception-based code and not-bad exception-based code. In particular, the example of bad vs not-bad doesn't show substantive differences between the two versions.

    // bad
    NotifyIcon CreateNotifyIcon()
    {
      NotifyIcon icon = new NotifyIcon();
      icon.Text = "Blah blah blah";
      icon.Visible = true;
      icon.Icon = new Icon(GetType(), "cool.ico");
      return icon;
    }

    // not-bad
    NotifyIcon CreateNotifyIcon()
    {
      NotifyIcon icon = new NotifyIcon();
      icon.Text = "Blah blah blah";
      icon.Icon = new Icon(GetType(), "cool.ico");
      icon.Visible = true;
      return icon;
    }
What is the problem with the first version and how is the second version fixing it?
alquemist··on AlphaFold: a solution to a 50-year-old grand challenge in biology
FWIW, transformers is to sequences what convnets is to grids, modulo important considerations like kernel size and normalization. Think of transformers as really wide (N) and really short (1) convolutions. Both are instances of graphnets with a suitable neighbor function. Once normalization was cracked by transformers, all sort of interesting graphnets became possible, though it's possible that stacked k-dimensional convolutions are sufficient in practice.
alquemist··on Cover Your Tracks
Testing page works for me now too. Could have been a temp fluke. Though, for example, https://amiunique.org/stats, ends up being an empty page.
alquemist··on Cover Your Tracks
Ironically, amiunique.org is broken with JS disabled.
alquemist··on Cover Your Tracks
Thanks. What is the most common user agent out there?

Sadly, this is of limited use. Defense against fingerprinting is like herd immunity. If everybody else already has a unique fingerprint, there is not much an individual can do to avoid being uniquely identified as well. At most one can spoof one other unique individual. Plus the EFF recommendation is 'latest Chrome on Windows' which is a moving target.

Would be nice if the EFF site in OP would recommend an agent id to spoof to, at least that would help building a small, but non trivial herd of indistinguishable users. And then a popular extension like uBlock Origin would track this agent id and set it by default for all its users.

Edit: list of top UAs:

https://techblog.willshouse.com/2012/01/03/most-common-user-...

alquemist··on Cover Your Tracks
And yet, not enough:

> Your browser fingerprint appears to be unique among the 2xx,xxx tested in the past 45 days.

> Currently, we estimate that your browser has a fingerprint that conveys at least 18.xx bits of identifying information.

Biggest offenders: USER AGENT and HTTP_ACCEPT HEADERS. Especially the USER AGENT is crazy, 9 digit browser version to everyone who asks?!

alquemist··on Cover Your Tracks
It does! I had to manually disable Javascript in UO settings, which solved the problem for me. FWIW, it doesn't solve the problem at scale, per opt-in / opt-out dynamics. It's a feature worth building into the browser, and setting it to disable JS by default. Make site owners ask for permission to use JS, and they better have a good reason.
alquemist··on Cover Your Tracks
Christmas came early this year :) Thanks!
Page 1 of 4Next →