Live data from Hacker News

Simplify your code: Functional core, imperative shell

testing.googleblog.com

201–210 of 219 posts

Re: Simplify your code: Functional core, imperative shell

#201
post #196
post #191

Earlier quoted context omitted.

Well you can claim that the core is the programming language, in which we write those tax rules, but that's not a very useful distinction IMHO (for how to write programs in the language).

That's only the case where a usable generic core already exists. A great example where it didn't exist is python's "requests" library: https://requests.readthedocs.io/en/latest/ The example on the homepage is the "specific shell" - simple and easy to use, and by far the most common usage, but if you scroll down the table of contents on the API page ( https://requests.readthedocs.io/en/latest/api/ ) you'll see section…

I like this example :) Another good example might be Git's distinction between "porcelain" and "plumbing"; the porcelain is implemented in terms of the plumbing, and gives a nicer* interface in terms of what people generally want to do with Git, but the plumbing is what actually does all the general, low-level stuff.

* opinions vary

Re: Simplify your code: Functional core, imperative shell

#202

Earlier quoted context omitted.

Not a single person in this thread commented on the use of Date.now() and similar - surely clock.now() - you never ever want to use global time in any code, how could you test it? clock in this case is a thing that was supplied to the class or function. It could just be a function: () -> Instant. (Setting a global mock clock is too evil, so don't suggest that!)

nit picking on the example code while missing the example the code was trying to demonstrate. I see why TAOCP used pseudocode

Agreed! But i didnt miss the example.... i also thought it was interesting that all the various examples of declarative or applicative did Date.now(), which i see as a big thing to avoid.

Re: Simplify your code: Functional core, imperative shell

#203

Earlier quoted context omitted.

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 fe…

Agree with zdragnar; this is not traditional DI which is generally focused on injecting objects.

The difference between the two is that when you inject an object, the receiving side must know a potentially large surface area of the object's behavior.

When injecting a function, now the receiving side only needs to know the inputs and outputs of the singular function.

This subtlety is what makes this approach more powerful.

Re: Simplify your code: Functional core, imperative shell

#204

Earlier quoted context omitted.

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 fe…

Agree with zdragnar; this is not traditional DI which is generally focused on injecting objects . The difference between the two is that when you inject an object, the receiving side must know a potentially large surface area of the object's behavior. When injecting a function, now the receiving side only needs to know the inputs and outputs of the singular function. This subtlety is what makes this approach more pow…

I don't see the difference, but I do agree that DI is generally used to mean constructing systems. It's what you do in your main or "bootstrap" part of the program and there are frameworks to do it for you. But really it's the same thing. You're just composing functionality by passing objects (functions are objects) that satisfy an interface. It might be more acceptable to just say it's dependency inversion.

Re: Simplify your code: Functional core, imperative shell

#205
post #108

Earlier quoted context omitted.

I disagree that these two pieces of advice are opposed. I think they are orthogonal at worst, and in agreement at best. "Functional core, imperative shell" (FCIS) is a matter of implementing individual software components that need to engage with side-effects --- that is, they have some impact on some external resources. Rather than threading representations of the external resources throughout the implementation, FC…

>GCSS tells us we shouldn't simply solve the one and only problem in front of us; we should use our eyes and ears and human brains to understand the context in which that problem exists This violates KISS and YAGNI and potentially leads to overengineering code and excessive abstraction

The context was "business", that kind of application is developed quite differently than, say, a cool little hobby terminal emulator or whatever.

Even though the business currently doesn't have a need to e.g. support any other currency than USD and EUR, an experienced developer will clearly see that it is unlikely to stay that way for long, so doing some preliminary preparation for generalizing currencies may well worth the time.

Re: Simplify your code: Functional core, imperative shell

#206
post #168

Earlier quoted context omitted.

> However, DDD has a strong object-oriented core The original 2003 DDD book is very 2003 in that it is mired in object orientation to the point of frequently referencing object databases¹ as a state-of-the-art storage layer. However, the underlying ideas are not strongly married to object orientation and they fit quite nicely in a functional paradigm. In fact, ideas like the entity/value object distinction are rather…

> The original 2003 DDD book is very 2003 in that it is mired in object orientation to the point of frequently referencing object databases¹ as a state-of-the-art storage layer. Irrelevant, as a) that's just your own personal and very subjective opinion, b) DDD is extensively documented as the one true way to write "good code", which means that by posting your comment you are unwittingly proving the point. > However,…

> "Underlying ideas" means cherry-picking opinions that suit your fancy while ignoring those that don't.

Yes, that is how terminology evolves to not meet a rigid definition that was defined in a different era of best-practice coding beliefs. I'll admit I had trouble mapping the DDD OO concepts from the original book(s) to systems I work on now, but there are more recent resources that use the spirit of DDD, Domain Separation, and Domain Modeling outside of OO contexts. You're right in that there is no single recipe - take the good ideas and practices from DDD and apply it as appropriate.

And if the response is "that's not DDD", well you're fighting uphill against others that have co-opted the buzzword as well.

- https://learn.microsoft.com/en-us/dotnet/architecture/micros... - https://www.infoq.com/news/2013/06/actor-model-ddd/

Re: Simplify your code: Functional core, imperative shell

#207

Earlier quoted context omitted.

I used to write lots of haskell before deciding it didn't meet my needs. However, the experience provided lots of long-term benefits, including a FCIS design mindset. Recently, I did a major python refactoring project, converting a prototype/hack/experiment into a production-quality system. The prototype heavily intermixed IO and app logic, and I needed to write unit tests for the production system. Even with fixture…

May I ask you how you model your functional code in Python, in absence of Haskell's algebraic data types?

Full algebraic data types wouldn't have added much here. Product types are already everywhere, and we didn't need sum or exponential types.

Splitting IO and pure code was just routine refactoring, not a full redesign. Our app logic wasn't strictly pure because it generates pseudorandom numbers and logs events, but practically speaking, splitting the IO and shell from the relatively pure app logic made for much cleaner code.

In retrospect, I consider FCIS a good practice that I first learned with Haskell. It's valuable elsewhere, even when used in a less formal way than Haskell mandates.

Re: Simplify your code: Functional core, imperative shell

#208
post #31

Earlier quoted context omitted.

I’d love to know more, do you have any links to your articles?

"Specific on the surface, generic underneath" (Medium paywalled): https://medium.com/tech-renaissance/generic-internals-specif...

> While internal modules and libraries should be kept as generic as possible, external-facing components, on the other hand, are a good place to put business-specific domain logic. External-facing components here refer not only to views but also to any kind of externally-triggered handlers including external API endpoints (e.g. HTTP/REST API handlers).

That goes against every bit of advice and training I've ever gotten, not to mention my experience designing, testing, and implementing APIs. Business logic belongs in the data model because of course the rules for doing things go with the things they operate on. API endpoints should limit themselves to access control, serialization, and validation/deserialization. Business logic in the endpoint handler—or worse, in the user interface—mixes up concerns in ways that are difficult to validate and maintain.

Re: Simplify your code: Functional core, imperative shell

#209

Earlier quoted context omitted.

I guess I just never encounter code like this in the big enterprise code bases I have had to weed through. Question. If you want to do one email for expired users and another for non expired users and another email for users that somehow have a date problem in their data.... Do you just do the const emails = three different times? In my coding world it looks a lot like doing a SELECT * ON users WHERE isExpired but in…

since were just making up functions.. myCoolSubroutine = do now The whole pipeline thing is a red herring IMO.

What language is this?

Re: Simplify your code: Functional core, imperative shell

#210
post #147
post #123

Earlier quoted context omitted.

> You really don't want And that's what this thread is filled with, and that's what I'm pushing back against. > the RAI pattern is nice > indicate if you are in different sections is nice Style doesn't matter, flavour doesn't matter, wants don't matter, code "quality" (whatever that means) doesn't matter, niceness doesn't matter. A transaction can be rolled back. If it can't, it's not a transaction.

I'd go a little further, though? Transactions have to be able to fail at the very last step. That could just be the "commit" stage. Everything up to that point could have been perfectly fine, but at commit it fails. More, the time of execution of that commit could be fairly far removed from the time of starting the transaction. To that end, any style that tries to move those two time periods closer together in code i…

> I'd go a little further, though? Transactions have to be able to fail at the very last step. That could just be the "commit" stage.

I don't know what you mean by going a little further. I said transactions are 'all or nothing', not 'all or nothing or 99%'.

Post reply on HN