News for hackers. Stuff that matters.

ljackman

Re:Project Loom and Structured Concurrency on Friday December 04, 2020 @12:00PM (#25304722)
That's a good point. Generally, I opt for languages that either compile away their nicities to avoid runtime hits, such as Rust being compiled to WASM, or languages that bring nicities in runtimes that they _completely own_, such as Java on the JVM.

The problem with Kotlin and the like is that they can't easily compile away their features due to inherent runtime dependencies, e.g. garbage collection, making them poorly suited to environments with a very minimal runtime like WASM, while also being at the mercy of the host language creating runtime abstractions that have mismatches with their own language's features.

Although it'd be unfair for me to say the JVM is designed only for Java; invokedynamic and non-reified generics both assist JVM targetting for non-Java languages such as Clojure.

Re:Project Loom and Structured Concurrency on Friday December 04, 2020 @11:41AM (#25304381)
`Async`/`await` or something like Kotlin's `suspend` are great language features for certain domains in which a developer needs to manage blocking system calls: in lower-level languages such as Rust or C, you probably don't want to pay for a lightweight "task runtime" Like Go's or Erlang's. They bring not only a scheduling overhead but also FFI complications.

However, for application languages that can afford a few extra nicities like garbage collection, I fail to understand why the stackless coroutine model (`suspend` in Kotlin) or `async`/`await` continue to be the developer's choice. Why do languages like Kotlin adopt these features, specifically?

Manually deciding where to yield in order to avoid blocking a kernel thread seems outside of the domain of problems that those using a _higher level_ language want to solve, surely?

The caller should decide whether to do something "in the background". And this applies to non-IO capabilities too, as sometimes pure computations are also expensive enough to warrant not blocking the current task.

Go and Erlang seem to have nailed this, so I'm glad Java is following in their footsteps rather than the more questionnable strategy of C# and Kotlin. (Lua's coroutines and Scheme's `call-with-current-continuation` deserve an honourable mention too.)

Re:From Node to Deno on Sunday May 17, 2020 @02:31PM (#23214092)
The whitelisting looks great. Even with the remaining concerns I raised in the other comment, the ability to whitelist only allowed domains for network connections is a massive step up security-wise, even if they are allowed for the entire program (until revoked globally).
Re:From Node to Deno on Sunday May 17, 2020 @02:21PM (#23213983)
That's a good start, supporting the dropping of privileges after performing something on startup.

What about keeping the network allowed in the layer handling, say, inbound HTTP connections, but blocking it in the data access layer or purely computational component?

From what I can see, this doesn't work with global boolean flags in the runtime, instead requiring isolated tasks with whitelisted capabilities passed in, some form of "immutable, set-once, dynamically-scoped capability flags", or something like that.

The problem with the global boolean flag approach is that if any part of a service needs it constantly, the entire program gets it, even obscure subdependencies for generating colour pickers.

Don't get me wrong, it's an incremental improvement over's Node.js blase approach. It's also quite niche to see languages support this feature. E was one of them. There was another newer Python-like language with this too, starting with an `M`, but its name escapes me.

I'd recommend Deno's developers look at E a bit more before committing too much to the platform boolean flag approach. Or I've misunderstood their approach and it actually does more than I'm giving it credit for.

Re:From Node to Deno on Sunday May 17, 2020 @01:49PM (#23213700)
It looks like a good evolutionary improvement over Node.js. However, I have a concern about its security claims. Perhaps someone on the project can allay these concerns.

My brief skimming of its site indicates that its security model is based around the ability to disable, say, network access for whole Deno programs. However, does it allow starting up with network access, allowing a subset of the program to handle it, and then dropping those rights for the rest of the program, _especially_ subdependencies?

I can't see any mention of a more fine-grained approach: https://deno.land/manual/getting_started/permissions

A modern Node.js web service will have hundreds, if not thousands, of indirect dependencies. Some network access will be required for at least the Express routing, or an equivalent.

For a Deno equivalent, this would amount to enabling network access for all hundreds of those subdependencies, not reproducing the isolation in capability-based security such as WebAssembly nanoprocesses.

Have I missed something here? That doesn't seem like much of an improvement over Node.js except for very small and contained programs. Yes, Deno's dropping of centralised package repositories and package.json might alleviate this problem _somewhat_, but the same fundamental issue seems to remain.

Re:Kotlin vs. Java on Tuesday December 17, 2019 @07:50AM (#21812961)
I wonder whether Project Loom will create a split in the Kotlin ecosystem between coroutine-based concurrency and Loom lightweight thread-based concurrency.

Java will have a similar problem with CompletableFuture and third-party projects like Reactor used in Spring WebFlux.

However, Kotlin baking in language syntax for a competing concurrency model will exacerbate the split.

With Java already having type inference, concise lambdas, and with upcoming records that can already be faked in current versions via Lombok, the value proposition of Kotlin seems questionable outside of Android with its held-back version of Java.

Clojure is another story entirely, offering a very different and compelling way of thinking about computing on the JVM. By contrast, my developing view is that Kotlin is different enough to require learning something new yet not different enough from modern vanilla java to offer substantial advantages.

Re:The Road to Valhalla on Saturday December 07, 2019 @03:02PM (#21732254)
There might be, but unfortunately I don't know it. I learnt Java initially from the first edition of Just Java, read from various other sources about the 1.5 and 1.6 additions it didn't cover, and then learned new features from OpenJDK proposals from 1.7 onwards as they were released.

The biggest impact of the 5 years for working developers has probably been:

* Streaming API and lambdas allowing usual `map`/`filter`/`reduce` transformation pipelines line many other languages (lazy, unlike JS's `Array` methods, parallelism considered via "spliterators" unlike Python).

* Default methods in interfaces, _not_ making them traits, but arguably more "trait-like". It allowed them to add many useful methods onto existing types without needing bifurcate the common APIs.

* A REPL. Probably not a big deal for a professional developer, who likely uses IntelliJ's "Evaluate Expression" feature already for a similar feature, but pretty useful for education I guess.

* Better at being stuffed inside Docker containers with resource limits. Also, max permgen space no longer a problem really (it's where you had to set an upper memory limit when invoking Java programs, which was annoying). Just generally less annoying at being deployed in modern infrasructures.

* APIs like `File.lines` that mean you don't need to recite War and Peace just to do a buffered operation across lines in a file. Generally more ergonomic APIs that finally make basic operations sane.

* Type inference with `var`. Basically the same as C#'s `var` or Go's `:=`.

* More native executable-producing tooling. jlink, jpackager, etc. The dream of ubiquitous, shared Java runtimes installed everywhere didn't really work out, and they seem to have realised that.

* Ecosystem moving away from XML. Still there, just less common. Runtime annotations are used more heavily, which I'm honestly not sure is a massive improvement. Hopefully more lambda-heavy APIs will reduce use of runtime annotations; see Spring WebFlux's functional handlers for annotation-based controllers for a comparison.

There's a lot more, but I'd argue it won't be as immediately visible for the working developer more than those points. Modules were important for the ecosystem, but most devs probably aren't worrying about that day-to-day. Gradle and Maven still dominate the building/packaging side.

Re:The Road to Valhalla on Saturday December 07, 2019 @09:01AM (#21730296)
Thanks for the clarification on the final NRT behaviour. Just to say, my point about "didn't align with good taste" was itself lacking taste. I'm sure each of those features I critiqued made sense as they were proposed at the time and were just considering different use cases and tradeoffs.

Did it drop the idea of assembly-wide opt-ins to stricter behaviour, meaning all NRTs can be reasoned about in the same way without considering a configuration flag somewhere like PHP? That does sound like an improvement.

Doing the change gradually, without breaking existing code or requiring potentially-ecosystem-breaking opt-ins does seem eminantly sensible and user-friendly. I was being too harsh to C# here. Java's `Optional` doesn't even warn about it itself being null, for example. You need static tooling and code analysis for that. C#'s solution does at least try to solve that, albeit at the cost of more complexity.

I said "monad-like" rather than "monadic" for that reason, but arguably it doesn't even go far enough to be considered monadic-like. Certainly this article would agree: https://blog.developer.atlassian.com/optional-broken/

Re:The Road to Valhalla on Saturday December 07, 2019 @08:44AM (#21730233)
Fair enough; I do miss keyword and default arguments from C# when using Java. A long list of overloads and argument forwarding to fake default arguments gets old.
Re:The Road to Valhalla on Saturday December 07, 2019 @08:41AM (#21730218)
FWIW, I could have produced an equally-long list of Java flaws. I'm not terribly attached to either language. I just wanted to challenge the meme that "C# is a better Java" I hear on the Internet every other week.

"Infinitely" more useful seems like a stretch. Both languages made valid design decisions with their lambdas: autogenerated types means not every variant of lambda needs a backing interface, but it also means that the types are a world unto themselves and not integrated with well-established interfaces and abstract classes in the way that Java lambdas are, resulting in conflicting mechanisms for passing code to adhere to a requirement. I find the unification of SAM types and lambdas to be elegant in a class-based OOP language (and actually preferable to the Smalltalk/Ruby block model too), but it's clearly subjective.

My criticism of properties hiding side-effects as attribute reads is mostly derived from one of the earliest books about the CLR; was it "The CLR via C#"? I'll have to check. The critique isn't Java-inspired; getters have the same problem of course. The point is that a getter is a method call, so you expect potential side-effects. You don't expect side-effects from a property read, although C# does tend to use capital letters for properties, to be fair.

While on the topic of CLR, I realise the DLR exposed dynamic typing primarily to make the CLR a better target for dynamic languages; I was arguing that exposing that up to C# wasn't necessary. C# is the flagship CLR language, sure, but that doesn't mean it must expose _every_ feature of it. Java also added `invokedynamic` for similar reasons but didn't feel the need to expose it to its flagship language directly in language syntax.

pron covered the nuances around generic type erasure in another comment better than I did, so I won't reiterate. Like you, I still mildly prefer reified generics over erasure as a language user, but the points raised by pron and Bracha are absolutely real. The ability to do runtime type checks and default values on generics seem like antipatterns, so I'm glad Java doesn't support those _specific_ features of reification even if I like a lot of the others parts.

Re:The Road to Valhalla on Friday December 06, 2019 @08:30PM (#21727832)
These C# issues arguably demonstrate haphazard additions that didn't align with good taste:

* Too many overlapping concepts for referring to code by value and defining them inline: events, delegates, anonymous delegates, and lambdas.

* Lambdas that generate magic types rather than slotting into SAM types. This works great for functional languages, sure, but doesn't fit well into a class-based OOP language.

* `ref` and `out` parameters to appease archaic COM APIs.

* Tuple unpacking, which makes sense independently but then bizzarely tries to integrate with `out` parameters.

* A nice "ex nihlo" object literal syntax that shoots itself in the foot by undermining immutability due to requiring settable fields. (TBF, later versions fixed this IIRC.)

* Inline functions in methods in a language that already has lambdas and methods; it's just bloat.

* `as` casting that yields nulls. Did it really need a whole new syntax just to handle null more concisely specifically for casting?

* `partial` classes. Encouraging even more code generation with features like this is a questionable idea and wasn't necessary in other languages.

* `dynamic`. Even if there are use cases for it, it's strange in an otherwise static language with a top-level Object type anyway. I'm saying this as someone who's perfectly happy with fully dynamic languages like Erlang and Lisp. I just don't see the point of adding it to C# specifically.

* C-style enumerations.

* Properties. Invoking side-effects on something that is syntactically indistinguishable from an attribute read is a bad idea. Auto-generation of Java's verbose getters would be long overdue, but the caller site shouldn't be the same a la C#.

* Extension methods. It seems quite ad hoc compared to static addition of types to common operations in other languages, like imported traits in Rust or typeclasses in Haskell.

* Proposed "shapes". Looks like a good idea by itself but will overlap too much with default method implementation and other existing mechanisms.

* Interfaces prefixed with `I`. Not strictly a language problem but an ecosystem one. I shouldn't need to know what _sort_ of type I'm dealing with, that's '90s Hungarian notation.

* Nullable reference types. Getting rid of null is good, but this proposal became confusing. They mentioned opting in assembly-wide for a while but there was then a conversation about having it just warn in some cases. I need to read the latest literature around this, but it seemed less elegant than Java just adding a monad-like Optional type and not adding loads of special-case operations with question marks everywhere.

Despite this, I still admire a lot of the design decisions behind C#. LINQ was great. `async/await`, despite my belief of its inferiority to Goroutines/Project Loom/Erlang processes, was still a great innovation from Midiori at the time. Value types were obviously right to be implemented early on. Assemblies were a good idea. Private by default rather than package-accessibility was a nice touch, as was the `override` keyword. The C# team are smart people who know what they are doing!

As an aside, I used to be firmly against erased generics, but reading more about the tradeoffs from the likes of Gilad Bracha has caused me to reconsider.

Re:The Road to Valhalla on Friday December 06, 2019 @06:39PM (#21727216)
While Java's slow and cautious evolution frustrates developers, it still arguably demonstrates longer-term thinking than the constant accrual of features in its contemporaries such as JavaScript and C#.

That isn't to say the designers of JavaScript and C# don't think carefully about the addition of new features; indeed, was it Anders Heljsberg who made the point about all new proposed features starting with negative points?

But Java's recent and upcoming additions show _taste_: adopting a single lambda notation that fitted smoothly with the surrounding class-based OOP paradigm via SAM types (rather than magically-generated-type-style lambdas succeeding two overlapping "delegate"/"event"-like features); implementing (sadly somewhat-incompatible) modules that can curtail unbounded reflection, opening the door to greater reasonability and performance in the future; proposing Project Loom to avoid the async/sync API split that hit Python, C#, and even Kotlin Coroutines; and now Valhalla, which was quietly mulled on for years before arriving at this very reasonable solution that considers myriad angles.

I like this approach to language design and think it bodes well for the language's future. It's a sweet spot between being necessarily conservative, dealing with developers' real-world problems, and giving time to mull over new language feature designs and not just implementing as soon (and haphazardly) as possible to please vocal developers.

Re:The Many Possibilities of iMessage File Vulnerability on Thursday August 22, 2019 @05:57PM (#20772768)
Agreed. A lot of serialisation support in mainstream languages seem to stand in stark contrast to the principles of langsec [1].

This bug reminded me a bit of Heartbleed actually; it’s pretty different but has that one similarity of two payload lengths, one reported and one actual, disagreeing and not being reconciled, causing memory bugs.

Even with memory safe languages, when you go beyond basic serialisation and look at libraries like Jackson for Java, you discover _very_ questionable designs such as providing an API that makes it all too convenient to switch on a feature which allows user-provided strings to control which classes to instantiate. They solve this using a blacklist of “known bad classes”, which seems to be a workaround rather than acknowledging that many features of common serialisation libraries are a fully loaded uzi pointing at the developer’s foot.

It seems common implementations of serialisation and deserialisation have prioritised ease of use for the developer over declaring clear, bounded intent, and this seems to apply from ad hoc C parsing of network payloads all the way up to automatic serialisation in memory-safe languages.

[1] http://langsec.org/

Re:The Many Possibilities of iMessage File Vulnerability on Thursday August 22, 2019 @05:37PM (#20772610)
I’m glad we’re seeing so many security vulnerabilities being exposed in the lower-level parts of the consumer OSes whose security we all take for granted. Doing writeups of compromised webapps is also great of course, but selling the importance of security to laypeople is easier as “the root of trust of your digital life was compromised in a certain way” than “a webapp for that service that only 2% of your country uses was compromised, so be sure to reset your passwords if you’re one of the unlucky ones”.

Non-techies take a lot of the core infrastructure and tooling for granted, not realising just what a heap of technical debt and accrued complexity it all is, and therefore just how hard it is to keep secure, despite the large security budgets and talents of the big FAANG companies.

It’s only by these lower levels being attacked and yielding bad PR for their parent companies will we eventually see less of a focus on new features for core ecosystem platforms and more of a focus on reducing the technical complexity and improving the security of what we have.

This will however need to be combined with technology journalism that’s more focussed on putting such vulnerabilities into accessible stories for the layperson and less focussed on being the unofficial marketing wing of technology companies on announcements.

Re:Why Const Doesn't Make C Code Faster on Tuesday August 20, 2019 @06:48AM (#20745449)
By ‘constant stack value’, I was referring to ‘effectively constant’ stack values, i.e. ones who aren’t reassigned in the scope or passed by mutable reference (or indeed are but aren’t actually modified by those functions anyway if the compiler can prove it).

My phrasing was probably a bit ambiguous there.

Re:Why Const Doesn't Make C Code Faster on Tuesday August 20, 2019 @06:46AM (#20745437)
You’re completely correct. I was conflating the ODR-adhering standard ‘inline’ keyword with the various inlining pragma in some compilers that actually are about solely forcing inlining as an optimisation.
Re:Why Const Doesn't Make C Code Faster on Monday August 19, 2019 @05:10PM (#20741859)
The key difference between `const` and the `register` and `inline` keywords is that, despite all of them often flagging optimisations that the compiler can often work out anyway, `const` still aids human comprehension by declaring constraints, whereas the latter two do not.

It seems that interprocedure optimisations in modern compilers make a lot of const-by-reference optimisations apply even when the code is mutable-by-reference in the parameter list but the function body doesn't modify it in practice. This would only work if it could deterministically work out which function is called.

Local constant stack values surely can be completely deterministically verified as such by the optimiser even without the `const` modifier. They could be overwritten without a C assignment to it, via the stack from a buffer overflow of an array next to it, but that's undefined behaviour so a compiler is presumably free to assume it is not modified, eliminate unnecessary register/memory loading code, and let the developers deal with the consequences.

As trailing `const` on member functions outlaws modifications via `this`, it would follow that the same optimisation-even-without-modifier process would apply as to `const` local stack values.

As constancy is a constraint that aids human comprehension, there's a good reason for choosing a keyword just as short as the mutable equivalent, such as Swift's `let` vs `var`; if the more constrained equivalent is equally or more convenient, more constrained and thus easy-to-reason-about code becomes more common.

Re:YAML: Probably not so great after all on Monday August 19, 2019 @08:25AM (#20737105)
Some templating languages such as Jsonnet[0] add built-in templating and just enough programmability to cover basic operations like templating and iteration.

I originally felt it was overly complex, but after seeing some of the Go text/template and Ansible Jinja examples in the wild, it actually seems like a good idea.

Perhaps we should more strongly distinguish between “basic” data definition formats and ones that need to be templated. JSON5 for the former and Jsonnet for the latter, for example.

Apple has been slowly phasing out GPLed software from their own software, such as Catalina's migration from Bash to Z Shell, but even writing third party apps with the GPL has apparently been problematic in the past: https://thenextweb.com/apps/2013/07/18/vlc-for-ios-will-retu...
Re:Jenkins Is Getting Old on Tuesday April 30, 2019 @06:02AM (#19786529)
My experience of Bamboo has included:

* Broken base Ubuntu images being recommended by Atlassian as the default for agent Image configuration, only to be fixed to a usable state months later;

* Being generally years behind other CI tools, even the traditional ones;

* Data exports corrupting themselves despite apparently succeeding, blocking migrations after upgrades or server changes;

* The official documentation somewhere recommending copying across password hashes directly for setting up new admin for a migration, but I can't find this anymore so they've hopefully improved the process and documentation for this;

* A bug in an earlier version in which a strange combination of error cases in an Elastic Bamboo image configuration could spam EC2 instances in a constant loop, which we thankfully spotted before it ate through our AWS bill;

* No clear messaging from Atlassian about how the future of Bamboo compares to Pipelines. The official line seems to be that Bamboo is for highly custom setups who want to host their CI themselves, but you get the impression from to the slow pace of development that they're slowly dropping it in favour of Pipelines. I'd rather they be honest about it if that is the case.

Those are just the problems I can think of from the top of my head, anyway.

I agree that CPython and V8 omitting the ability to juggle multiple stacks is a mistake. For higher-level languages, undelimited coroutines or continuations allow for very useful abstractions like Go's and Erlang's transparently non-blocking IO.

However, it doesn't seem to be _entirely_ an implementation detail. Some developers just don't seem to like the semantics of called functions being able to cooperatively yield without the caller explicitly opting into it with a keyword like `await`. I disagree with them, but it's a legitimate complaint I've a few times.

It reminds me of arguments in the Lisp community about delimited continuations and undelimited, i.e. Common Lisp and Scheme. A lot of the arguments there are really about semantics and not implementation details, and come to the same point: should cooperative scheduling require explicit notation at each level of the call stack?

My view on this is that systems languages like C and Rust should require explicit notation for it whereas application languages should not. This seems to be a point in favour of Go and Erlang over Java and C#.

However WASM, similar to C or Rust, seems to target a level in the tech stack at which it should concern itself only with abstractions that have relatively direct translations to the instruction set of the underlying hardware. Support for multiple stacks doesn't fit into this from what I can see. (A similar argument can be made for WASM not supporting garbage collection too, although it looks like that'll be added at some point to make interoperability with JS smoother.)

With the JVM supposedly adding fibres soon, it poses a question for WASM: is it trying to be a portable assembly language, a portable high-level language runtime, or something in between?

Most common coroutine implementations, such as JavaScript's and Python's, are delimited or "symmetric". This means the most obvious implementation is in terms of compiler transformations in the source language. It seems out of WASM's scope to do this.

Undelimited "asymmetric" coroutines, like Lua's, could be an interesting addition. That still seems to me to be too high level a feature for a "portable assembly language" specification though.

Yes, macOS seems to be heading in the right direction (even if it means frustrating legitimate usage patterns at times).

In particular, I like that it kept it simple by either an application being sandboxed with explicit user permissions for certain features, or not being sandboxed at all. Contrast with Linux FlatPaks where even "sandboxed" programs can have a wide range of implicit permissions based on the image configuration, which aren't obvious to end users when running `flatpak install` in the same way that macOS's privacy settings are for each sandboxed macOS app.

I remember some FlatPak applications defaulting to allowing unsandboxed home directory access without prompting me on installation, which seemed to defeat the purpose somewhat. It does block nefarious control flow attacks against programs that declare a strict sandbox, but that doesn't seem to go far enough in my view.

Good point. The lack of visibility into today's blackbox commodity hardware is frightening, such as Intel ME. It's even worse when that hardware is listening to external commands via the EM spectrum and the like, as you point out.
Re:An Experimental Port of Clojure to Common Lisp on Sunday January 13, 2019 @12:56PM (#18897765)
This approach being even viable is a testament to the host platform portability of Clojure, but even more to Common Lisp's ability to read other language's token-level syntax via reader macros (unless I've misunderstood how this projects ports Clojure to CL).

Reader macros in CL allow it to implement much of Clojure, but Clojure's lack of them means it could never implement CL unless it implemented built-in readers to handle every possible reader macro that a CL implementation loaded in.

As arbitrary library code can access the reader in CL, this would apparently be impossible.

I suppose it comes down to whether a Lisp dialect wants to support arbitrarily-powerful metalinguistic abstractions in its own Lisp syntax or provide the ability to create whole new languages with different fundamental tokens.

If a dialect only wants to achieve the former but has no interest in the latter, AST macros would suffice and custom reader macros would be of little interest, which is presumably the reasoning behind Clojure's lack of them.

Anyway, kudos for a great project.

Most laptop OSes, like Windows, macOS, and Linux, are a decade behind mobile OSes in terms of application sandboxing and also still lagging behind on restricting OS tampering and implementing reliable chains of trust from the system software to the hardware level, e.g. features like secure enclaves.

Unfortunately, those restrictions also make mobile OSes less useful for the sort of technical work that people use "real" OSes for. However, those restrictions make such devices much more compelling roots of trust for our digital lives.

An iOS app can't extract my contacts unless I grant permission, yet an application installed on Ubuntu via `apt` can casually start rummaging around my home directory which I won't know about unless I spend considerable time on mandatory access control profiles, isolation through containerisation or virtualisation, or something equally esoteric for the average user.

I suppose a phone does hold more sensitive information though, like location and mobile payments, making it a more lucrative target.