Live data from Hacker News

Simplify your code: Functional core, imperative shell

testing.googleblog.com

91–100 of 219 posts

Re: Simplify your code: Functional core, imperative shell

#91

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…

That's pretty hardcore, like you want to restrict the runtime substitution of function calls with their result values? Even Haskell doesn't go that far. Generally you'd distinguish which function call introduces the error with the function call stack, which would include the location of each function's call-site, so maybe the "low-effort" label is accurate. But I could see a benefit in immediately knowing which funct…

> you want to restrict the runtime substitution of function calls with their result values?

I don't get how you got there from parent comment.

Pascal just went with a needless syntax split of (side-effectful) methods and (side-effectful) functions.

Re: Simplify your code: Functional core, imperative shell

#92

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…

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.

Re: Simplify your code: Functional core, imperative shell

#94
post #16

This works right up to the point where you try to make the code to support opening transactions functional. :D Some things are flat out imperative in nature. Open/close/acquire/release all come to mind. Yes, the RAI pattern is nice. But it seems to imply the opposite? Functional shell over an imperative core. Indeed, the general idea of imperative assembly comes to mind as the ultimate "core" for most software. Edit:…

> This works right up to the point where you try to make the code to support opening transactions

Indeed. It's all well and good to impart some kind of flavour into your code and call it functional, but transactions do not give a crap about style.

A transaction needs to be able to 'back out' to fulfill 'all-or-nothing' semantics. Side effects are what make this impossible.

Re: Simplify your code: Functional core, imperative shell

#95
post #11

Earlier quoted context omitted.

The example in the wiki page is far more rudimentary than the ones I encountered when I was shown this concept. Trivial, in fact. CQS will rely on composition to do any If A Then B work, rather than entangling the two. Nothing forces composition except information hiding. So if you get your interface wrong someone can skip over a query that is meant to short circuit the command. The constraint system in Eiffel I don’…

That's still not really answering my question for you, which was less clear than intended. To restate it: > The one place this advice falls down is security - having functions that do things without verifying preconditions are exploitable My understanding of your comment was that "this advice" is CQS. So you're saying that CQS commands do not verify preconditions and that this is a weakness in CQS, in particular. Whe…

And I remain confused by your confusion.

If A then B()

Versus

B()

Somewhere there’s a B without the associated query. Call it what you want, at the bottom of the tree two roads diverge. Otherwise there is no Separation in your CQS.

ETA: once you get down to the mutation point you aren’t just dealing with immutable data. You’re moving things around, often plural.

Re: Simplify your code: Functional core, imperative shell

#96
Another good blog post that is IMO in the same vein: https://lambdaisland.com/blog/2022-03-10-mechanism-vs-policy (“Improve your code by separating mechanism from policy”). This blends harmoniously with “functional core, imperative shell”—the "mechanism" code is the "functional core", and the "policy" code is the "imperative shell"—and also a little bit with John Ousterhout's idea in A Philosophy of Software Design of "deep modules" (in this context, don't put policy stuff, i.e. arbitrary decisions, inside the module).

Re: Simplify your code: Functional core, imperative shell

#97

I like the idea but the example doesn't make much sense. In what application would you load all users into memory from database and then filter them with TypeScript functions? And that is the problem with the otherwise sound idea "Functional core, imperative shell". The shell penetrates the core. Maybe some filters don't match the way database is laid out, what if you have a lot of users, how do you deal with email b…

I am surprised by this example, for the same reason.

Generally, performance is a top cause of abstraction leaks and the emergence of less-than-beautiful code. On an infinitely powerful machine it would be easy and advisable to program using neat abstracrions, using purely "the language of" the business. Our machines are not infinitely powerful, and that is especially evident when larger data sets are involved. That's where, to achieve useful performance, you have to increasingly speak "the language of" the machine. This is inevitable, and the big part of the programmer's skill is to be able to speak both "languages", to know when to speak which one, and produce readable code regardless.

Database programming is a prime example. There's a reason, for example, why ORMs are very messy and constitute such excellent footguns: they try to gap this bridge, but inevitably fail in important ways. And having and ORM in the example would, most likely, violate the "functional core" principle from the article.

So it looks like the author accidentally presented a very good counterexample to their own idea. I like the idea though, and I would love to know how to resolve the issue.

Re: Simplify your code: Functional core, imperative shell

#98
I have written a small system in Elixir adhering to FCIS. Not used to the approach, I was pretty slow and sometimes it felt like jumping through hoops set by myself, lol, but I loved it, the code was very clean, testable, and refactorable. Highly recommend it as an exercise, it was surprising just how much state and IO can be pushed out.

Re: Simplify your code: Functional core, imperative shell

#100

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…

> Even large companies are still grasping at straws when it comes to good code 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. Adding functionality often requires more hacks. The alternative is to fix the mess, but that's not part of the task at hand.

> 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 specific point of view. But points of view are also subjective.

Take for example domain-driven design. There are a myriad of books claiming it's an effective way to generate "good code". However, DDD has a strong object-oriented core, to the extent it's nearly a purist OO approach. But here we are, seeing claims that the core must be functional.

If OP's strong opinion on "good code" is so clear and obvious, why are there such critical disagreements at such a fundamental levels? Is everyone in the world wrong, and OP is the poor martyr that is cursed with being the only soul in the whole world who even knows what "good code" is?

Let's face it: the reason there is no such thing as "good code" is that opinionated people making claims such as OP's are actually passing off "good code" claims as proxy's for their own subjective and unverified personal taste. In a room full of developers, if you throw a rock at a random direction you're bound to hit one or two of these messiahs, and neither of them agrees on what good code is.

Hearing people like OP comment on "good code" is like hearing people comment on how their regional cuisine is the true definition of "good food".

Post reply on HN