Live data from Hacker News

Simplify your code: Functional core, imperative shell

testing.googleblog.com

151–160 of 219 posts

Re: Simplify your code: Functional core, imperative shell

#151
post #148

Earlier quoted context omitted.

So would you then have to do `getActualUsers(db.getUsers())` or `query(db.getUsers())`? Still smells like in such a case the developer avoids the complications of abstraction or OOP by making the user deal with it. That's bad API design due to putting ideology before practicality or ergonomics.

You would have an API that makes the query shape, the query instance with specific values, and the execution of the query three different things. My examples here are SQLAlchemy in Python, but LINQ in C# and a bunch of others use the same idea. The query shape would be: active_users = Query(User).filter(active=True) That gives you an expression object which only encodes an intent. Then you have the option to make bas…

This is a better story because it has consistent semantics and a specific query structure. The db.getUsers() approach is not part of a well-thought-out query structure.

Re: Simplify your code: Functional core, imperative shell

#152

I wrote our AI agents code with a functional core + imperative shell and I have to agree: this approach yields much faster cycle times because you can run pure unit tests and it makes testing a lot easier. We have tens of thousands of lines of code for the platform and millions of workflow runs through them with no production errors coming from the core agent runtime which manages workflow state, variables, rehydrati…

Great examples. We were taught to pass variables, scalar or compound, into API's. Most of us were never taught to pass functions. Even Python examples in trainings that look functional might not be. They put the function calls in as arguments. The beginner thinks the function returns some data, that would be in a variable, and they are implicitly passing that variable. Might as well, for readability, do the function…

It's called dependency injection and there's loads written about it. It's a really powerful technique which is also used for dependency inversion, key for decoupling components. I really like how it enables simple tests without any mocking.

The book Architecture Patterns in Python by Percival and Gregory is one of the few books that talks about this stuff using Python. It's available online and been posted on HN a few times before.

Re: Simplify your code: Functional core, imperative shell

#153
post #133

While I largely agree with the philosophy, the example provided is not very practical. The code snippet `getExpiredUsers(db.getUsers(), Date.now())` is unlikely to occur in real-life scenarios. No one would retrieve all users and then filter them within the program. Instead, it should be `db.getExpiredUsers(Date.now())`. We should never be too extreme on anything, otherwise it would turn good into bad.

> No one would retrieve all users and then filter them within the program. No one _should_ do that, but that's a common enough problem (that usually doesn't get found until code is running in production). I suspect with the rise of vibe coding, it's going to happen more and more.

Sometimes it's forced by using the wrong database in the first place, or the wrong data structure. It can be less pain to do a bit of post-processing in the application layer than to unpick either of those.

Re: Simplify your code: Functional core, imperative shell

#154
post #139

Earlier quoted context omitted.

> Probably many reasons for this, but what I've seen often is that once the code base has been degraded, it's a slippery slope downhill after that. Another factor, and perhaps the key factor, is that contrary to OP's extraordinary claim there is no such thing as objectively good code, or one single and true way of writing good code. The crispest definition of "good code" is that it's not obviously bad code from a spe…

> However, DDD has a strong object-oriented core, to the extent it's nearly a purist OO approach Really? https://fsharpforfunandprofit.com/ddd/

Yeah, I haven’t read Scott’s books, but my understanding of ddd is such that it should be extremely applicable to DDD.

DDD is described in terms of OOP, but really imo it makes far more sense in fp contexts.

Re: Simplify your code: Functional core, imperative shell

#156

Even large companies are still grasping at straws when it comes to good code. Meanwhile there are articles I wrote years ago which explain clearly from first principles why the correct philosophy is "Generic core, specific shell." I actually remember early in my career working for a small engineering/manufacturing prototyping firm which did its own software, there was a senior developer there who didn't speak very go…

> Meanwhile there are articles I wrote years ago which explain clearly from first principles why the correct philosophy is ... I think this is a very common mistake. You've spent years, maybe decades, writing code and now you want to magically transfer all that experience in a few succinct articles. But no advice that you give about "the correct philosophy" is going to instantly transfer enough knowledge to make all…

> But no advice that you give about "the correct philosophy" is going to instantly transfer enough knowledge to make all large companies write good code, if only they followed it.

Good old "Programming as Theory Building". It's almost impossible to achieve this kind of transfer without already having the requisite lived experience.

[0]: https://ratfactor.com/papers/naur1_theory_building.pdf

Re: Simplify your code: Functional core, imperative shell

#157

This sounds to me like the old hexagonal architecture [1] [1] https://en.wikipedia.org/wiki/Hexagonal_architecture_(softwa...

Yep! I remember the phrase "functional core, imperative shell" being popularized by Gary Bernhardt in ~2012 [0][1]; in his talk Boundaries [0] (around 31:00), he even mentions "hexagonal architecture" by name.

[0]: https://www.destroyallsoftware.com/talks/boundaries

[1]: https://www.destroyallsoftware.com/screencasts/catalog/funct...

Re: Simplify your code: Functional core, imperative shell

#158
post #148

Earlier quoted context omitted.

You would have an API that makes the query shape, the query instance with specific values, and the execution of the query three different things. My examples here are SQLAlchemy in Python, but LINQ in C# and a bunch of others use the same idea. The query shape would be: active_users = Query(User).filter(active=True) That gives you an expression object which only encodes an intent. Then you have the option to make bas…

As in Django querysets. But starts to get messy with complex queries.

It can. SQLAlchemy has good support for types since 2.0, which helps a lot.

Re: Simplify your code: Functional core, imperative shell

#159
post #92

Earlier quoted context omitted.

These are great and succinct, yours and your teammate’s. I still find myself debating this internally, but one objective metric is how smoothly my longer PTOs go: The only times I haven’t received a single emergency call were when I left teammates a a large and extremely specific set of shell scripts and/or executables that do exactly one thing. No configs, no args/opts (or ridiculously minimal), each named something…

I see a similar problem in infra-land where people expose too many config variables for too many things, creating more cruft. Knowing what to hardcode and what to expose as a var is something a lot of devs don't seem to understand; and don't realise they don't understand.

Oh definitely, many headaches untangling massive “variables.tf” files where the value is identical in 100% of the target environments, and would be nonsensical to change without corresponding changes in the infra config resources/modules as well.

My favorite are things where security policy mandates something like private networking and RBAC, and certain resources only have meaning in those contexts, for heavens sake why are we making their basic args like “enforce_tls” or “assign_public_ip” or “enable_rbac” into variable params for the user to figure out

Re: Simplify your code: Functional core, imperative shell

#160

I never liked encountering code that chains functions calls together like this email.bulkSend(generateExpiryEmails(getExpiredUsers(db.getUsers(), Date.now()))); Many times, it has confused my co-workers when an error creeps in in regards to where is the error happening and why? Of course, this could just be because I have always worked with low effort co-workers, hard to say. I have to wonder if programming should ha…

These chains become easy to read and understand with a small language feature like the pipe operator (elixir) or threading macro (clojure) that takes the output of one line and injects it into the left or rightmost function parameter. For example: (Elixir) "go " |> String.duplicate(3) # "go go go " |> String.upcase() # "GO GO GO " |> String.replace_suffix(" ", "!") # "GO GO GO!"

(Clojure) ;; Nested function calls (map double (filter even? '(1 2 3 4)))

;; Using the thread-last macro (->> '(1 2 3 4) (filter even?) ; The list is passed as the last argument (map double)) ; The result of filter is passed as the last argument ;=> (4.0 8.0)

Things like this have been added to python via a library (Pipe) [1] and there is a proposal to add this to JavaScript [2]

1: https://pypi.org/project/pipe/ 2: https://github.com/tc39/proposal-pipeline-operator

Post reply on HN