HN user

joostdevries

263 karma

https://joost-de-vries.bsky.social

Posts3
Comments99
View on HN

I'm experimenting with OpenShell / NemoClaw.

It brings some extra sandboxing features compared to just a docker container.

For instance it also maps the secrets that the agent harness has access to. And has an allow list for outgoing http connections. And there's a way for agents to request extra access. And once approved by the user from an external cli the policy is updated and hot reloaded.

It's pretty recent. So time will tell how robust it is / will become. What its longevity will be. After all; in a time of LLMs one off experiments are cheap. Longtime nurturing not so much.

Maybe the pitch is:

git is distributed. Decentralised improvement. Local computers and their users make changes. These steps of local added value are then centrally combined into a shared timeline. A single product. During the improvement the locus of control is local. Which means it is hard to harvest the knowledge of this local knowledge and replace it. And it's hard to make local users serve the central AI.

Not something you put in the public mission statement. Because you might get boycotts.

I think Americans pronounce names like Weinstein as 'winesteen'. So perhaps the pronunciation of the latter syllable makes German origin diphthongs confusing.

The need for flags in itself may be a sign of the wrong approach. You create a shared module. But for some cases the handling should be different. Let's pass in a flag or configuration. So somewhere in the shared component it will handle things differently. Oh wait, there's another case. Another flag. Which combinations of flags are meaningful and which aren't? The nearest improvement is to put all the cases in an enumeration. But wait it's not one 'dimension'. Let's have two enumerations. And the extreme end point is that your shared module has a full blown DSL for the parameters it receives. Or even an interpreter. The other solution that is often more understandable and debuggable in the long run is to not have the shared module call specific code for cases. But to have specific code pull in shared commonalities. No need for flags.

Are you in the EU? I'm a developer in the EU and that is patently not true. Developers have to have mechanisms in place to delete gdpr data when required and not store data that's not required for you goals. In my experience gdpr puts a real and meaningful curb on the strong impetus to gather everything and sell it.

Your word of caution is important. For some projects it's not initially clear what the right data model and bounded contexts are. And for some projects it's just overhead. But the converse is also true: for some projects/systems it turns out that it being events first is the _only_ way for it to work. I've encountered that the last few years with systems in logistics that show an integral view across parties.

The way we approached that is that we started with a standard system and refactored to event processing approach when the decomposition into bounded contexts was clear.

So in that sense it's similar to the right way of approaching microservices: start out with a 'monolith' that you split up.

The monadic approach to exceptions like it's done here has the downside of having to encode all your business logic in maps and flatmaps. All the intermediary functions will have to return Try. And all the Try values have to combined into one. That type fidding code feels like cruft that doesn't add much value.

And I say that having developed in Scala for years and years.

Of course there's still sometimes when you want processing of other items to proceed, even though this one has failed. And that's where Try is really essential. So that's where we use it only: at the topmost level.

It's only at that top level that we wrap the call in a Try. And that works rather well.

One reason to do it like this is that we want to leverage the design philosophy of Kotlin. And not be writing Scala or Haskell in Kotlin ). And Kotlins philosophy when it comes to exceptions turns out to be: exceptions. Which is apparent in Kotlin coroutines, channels and flows.

) that's also why we try to stay away from Arrow

Kotlin vs. Java 7 years ago

I've developed in Scala for 5 yrs, last yr in Kotlin. Scala spearheaded from 2007 a lot of features that nowadays are the norm for statically typed programming languages. Odersky is a visionary imo. For a while I experienced Kotlin as Scala--. (No pattern matching?!? ) A bit later I discerned where Kotlin is treading its own path. The biggest things for me are: non monadic approach to async. Which works really well. And simpler extension functions. I'm looking forward to trying out union types that are coming to Scala.

I agree. Naming parts of your code is important. An alternative to named local vals is to either use named functions instead of lambdas: people.filter(olderThan50) or (I use Kotlin) use named extension functions: people.countOlderThan50()

As another Utrechter I'm really impressed with the large bicycle garages on either side of the new railway station. It really makes cycling from and to the station a great experience. There are quite a few grand new railway stations that have been built in the NL: Rotterdam central station, Utrecht central station, Arnhem station. Recently I was moved when I realised that they're the cathedrals of our time. Shared architectural spaces that are big communal efforts and will be of great value for all for decades to come.

Java 12 7 years ago

I find checked exceptions interesting. They happen to be the only example of an effect system in a mainstream language. Anders Hejlsberg formulated the main argument against them here [1] Basically the problem is that checked exceptions are infectious and intermediary code needs to annotate all the exceptions of code that it calls. Which is a chore. Lucas Rytz states in his PhD thesis [2] that the problem is not wich checked exceptions themselves, but that in the java implementation 'not mentioning an exception means if won't happen'. He proposes a system where the default is 'any exception' and being able to turn it off with a compiler flag. And states that the developer experience of that would be much better. This may become possible in Dotty Scala using the effect system based on implicit functions. But that's in 'proposed' status.

[1]https://www.artima.com/intv/handcuffs.html [2]https://lrytz.github.io/pubsTalks/ the link to his thesis doesn't work anymore. I cherry pick some of it in my talk for a Scala meetup in Utrecht https://www.slideshare.net/yoozd/effect-systems-in-scala-bey...

We've been using mobx-state-tree too. What I like is that it's easier to gain an understanding of the moving parts of my application; how actions, derived data and data work together. On the other hand the support for Typescript is a bit clunky and leads to slow compilation. Mitigating the compile time leads to a bit of boiler plate.

Being able to take an instance Person and make it be an Employee without changes to the Person class definitely sounds like ad hoc polymorphism. In Scala the equivalent of Haskells type classes is implicits though. Not traits. I do wonder if this 'extension interface implementation' feature would support the same inferencing that type classes in Haskell and implicits in Scala do. So that might be a difference.

Interesting, the new type IAsyncEnumerable is like an Observable. But it's pull instead of push. Ie it supports backpressure like the Reactive Streams [1] standard in the JVM world. I seem to recall that Erik Meijer had some strong opinions when that standard was forming on backpressure and anything that's not purely push based.

Personally as a developer I really like having the choice whether to buffer in one place in my system (using a persistent queue f.i.) and have the rest of the system cooperate in tandem without running out of memory f.i. Or to choose to ignore elements when stuff is just going to fast. Etc. I feel more in control of the behaviour under load of my system.

I guess Lightbend was onto something when they took the old EAI notion of backpressure (as f.i. described in Gregor Hohpes book) and implemented it in stream processing and started an interoperability standard around it.

[1] http://www.reactive-streams.org/