Live data from Hacker News

Modern Functional Programming: The Onion Architecture

degoes.net

61–70 of 100 posts

Re: Modern Functional Programming: The Onion Architecture

#61
post #37
post #32

Earlier quoted context omitted.

You are referring to, for example: http://clojure.org/reference/transients Combining the two ideas Transient imperative logic in the core (5%), Functional mantle (90%), Side-effecting imperative crust (5%).

Yes, because some purely functional approaches cannot beat imperative ones when it comes to resource usage.

Could you give a concrete example?

Re: Modern Functional Programming: The Onion Architecture

#62

Earlier quoted context omitted.

This kind of abstraction layer is really challenging though unless you're totally aware of the extent of your imperative code's side effects.

Isn't this already happening, though, on a low-level (i.e. ASM) ?

CakeML, a Standard ML subset, can compile to ASM with mathematical proof of correctness. You know your ASM will be what you expected it to be. There's also tools like QuickCheck, QuickSpec, and SPARK that can automate lots of analysis. Tons of work like this in compilers and static analysis with smart people always improving them.

Good chance your business logic will not be handled that well. So, good to structure it in a way to facilitate easy analysis or optimization by tools that currently exist or are in development. You get long-term benefits.

Re: Modern Functional Programming: The Onion Architecture

#63
post #37

Earlier quoted context omitted.

Yes, because some purely functional approaches cannot beat imperative ones when it comes to resource usage.

Could you give a concrete example?

Most in-place algorithms. E.g. quicksort

You can do merge sort in Haskell asymptotically as well as C, but not quicksort (because you can't mutate things in place).

I am of course omitting things like ST which do give you this sort of ability in Haskell, but I doubt that's what the OP meant by "purely functional".

Re: Modern Functional Programming: The Onion Architecture

#64
post #60

Earlier quoted context omitted.

My own opinions on the matter aside, I don't fully understand why anyone who likes dynamic scoping would dislike dependency injection.

Because of things like: https://github.com/google/guice 4k LOC of "lightweight" garbage for... variable lookups?

To put that into perspective, the Squeak interpreter for the gold standard of OOP languages, Smalltalk, was about 3951loc of Smalltalk for logic to handle language and 1681loc of C for OS interface. A lightweight scheme for dependency injection took them more loc to express than a whole Smalltalk interpreter. And to hack around bad OOP or tooling in the first place.

This might not mean anything. It just jumped out in my brain for some reason.

Re: Modern Functional Programming: The Onion Architecture

#65
post #33

Earlier quoted context omitted.

I like the 'weakly pure' concept in D. A function like pure int frignate(database db, const config cfg); cannot change anything except the database object (and anything reachable from it). Can not mutate the environment. Can not mutate the config object parameter. This is finer control than pure functional programming and safer than imperative/object-oriented programming.

I'm not sure how D's purity system works, but that doesn't seem very pure to me. Mutating the database object is a globally visible effect, after all.

The guarantee it's making is that it's not going to manipulate program state that is outside the scope of "db". That is a pretty big deal for a systems developer since, as I discovered while working with D, many standard library functions that you wouldn't have given a second thought about happen to do unpure things like set a processor flag. We aren't even talking about your own program.

From an abstracted viewpoint, it's not great, since a whole database covers potentially a lot of scope, and you may not want to care about the details of your floating point calculations in hardware, but in a concrete sense this is totally correct!

Re: Modern Functional Programming: The Onion Architecture

#66
post #2

I'd not say this is a feature of functional programming. This is a feature of Object Oriented elements being included in fp languages. These are the same things we were going to happen when using Java and C++ years ago. You can even see similar graphics here: https://docs.oracle.com/javase/tutorial/java/concepts/object... I remember there was another in this tutorial that shared more with the image in this post. Alth…

It started in CompSci sort of in parallel with Bob Barton's B5000 designed for ALGOL, the work of Dijkstra, McCarthy's LISP, Hamilton's USL, and so on. They each came up with abstract ways to specify or implement programs for greater correctness, readability, safety, and composition. Using provers, compilers, or manual work, these would be converted into specific code at lower levels or final, machine level in ways intended to preserve high-level properties. Dijkstra went furthest in THE where the did hierarchical layers that were loop free. In parallel, there was a group trying to figure out how to compile Monte Carlo simulations on their computers in a way that was easy to specify. Their Simula was first, OOP language. SimScript, a RAND language, was a discrete, event simulation tool that came out with many similarities to emerging Simula and OOP. The main programmer had already been exposed to ALGOL, loved the heck out of it, and took some inspiration from SimScript.

Hierarchical layering, careful interfaces, dynamic programming, functional composition, refinement, event-based simulation... all showing up before Simula was published in 1967. So, it anywhere from depended on to came after many things with properties key to OOP's effectiveness. It certainly got a powerful technique for structuring programs started but didn't happen in isolation or even necessarily ahead in many ways. It surprised me that needs of Monte Carlo apps is what led to OOP but not that ALGOL60 was involved.

http://phobos.ramapo.edu/~ldant/oop/simula_history.pdf

https://www.rand.org/content/dam/rand/pubs/research_memorand...

Re: Modern Functional Programming: The Onion Architecture

#67
post #33

Gary Bernhardt describes a similar architecture using a "Functional Core, Imperative Shell" in his Boundaries talk[1]. "Purely functional code makes some things easier to understand: because values don't change, you can call functions and know that only their return value matters—they don't change anything outside themselves. But this makes many real-world applications difficult: how do you write to a database, or to…

I like the 'weakly pure' concept in D. A function like pure int frignate(database db, const config cfg); cannot change anything except the database object (and anything reachable from it). Can not mutate the environment. Can not mutate the config object parameter. This is finer control than pure functional programming and safer than imperative/object-oriented programming.

You can achieve this level of control using monad transformers or extensible effects -- this is a standard technique in Haskell.

    frignate :: (MonadDB m) => Config -> m Int
    frignate cfg = do
      db 
And it is composable, so if a function calls a function that uses one of the managed resources then the requirement propagates upward.

And you can swap in non-IO based instances for testing, or whatever else you want.

Re: Modern Functional Programming: The Onion Architecture

#68

Earlier quoted context omitted.

> Beginning at the center, each layer is translated into one or more languages with lower-level semantics. ORM mapping into Java? An ORM would not have lower-level semantics would it?

Lower in this context does not mean 'weaker'. An ORM maps between the lower level relational semantics and higher level object semantics.

Nah, if it's this topic, more like how AllegroCache object-database maps high-level commands on database manipulation in LISP that look similar to regular manipulation of objects to lower-level LISP that's more efficient which compiles to machine code that efficiently implements those structures on target CPU. Typical ORM's take two things that are very different from one another and never intended to match up then try to force them to match up. Not a great example.

Here's another for you: Lilith. Assembly is messy. So, they create a stack machine (M-code - P-code variant) that idealizes it plus easily compiles to it or implementable at CPU level. Then, they create a 3GL called Modula-2 that's closer to how they express programs but has underlying model of M-code and outputs M-code. In theory, they could go further like 4GL's did to build domain-specific languages that abstract away boiler plate with code generation but still consistent with Modula-2 style. And so on. Easier in functional languages but I'm sure you can see the pattern.

That's what you usually get out of the DSL or interpreter approaches if using a LISP, Haskell, or imperative language designed to make it easy. Languages designed to do them well. A series of transforms with a certain amount of consistency from start to finish. These days, there's even safety techniques for the transforms that they didn't have way back in the day. :)

Re: Modern Functional Programming: The Onion Architecture

#69

Earlier quoted context omitted.

There is a very small amount of boilerplate. And I would argue strongly that it's a good thing ; it indicates to the reader of the code that an object's behavior reads from some initialized value, or equivalently that its behavior depends on some initial value which remains fixed through the computation. The reader monad gives you a simple language to express this common pattern, as well as the ability to easily set…

What if `function2` needs to log something? Without dependency injection, you need to pass that logger to the function. With dependency injection, that logger is available without having to pollute the method signature with an implementation detail.

"Implementation detail"

We are in strong disagreement about what constitutes an "implementation detail".

But also, you can just use a monad transformer stack and add whatever side-effectful operations you want into it, use it as needed. Boom, dependency injection. And more control over what your functions actually do is there when you need it.

Re: Modern Functional Programming: The Onion Architecture

#70
post #37

Earlier quoted context omitted.

Yes, because some purely functional approaches cannot beat imperative ones when it comes to resource usage.

Could you give a concrete example?

Another example: Caching of results, transparent to the user of your function. If your function is otherwise pure you can simply use its parameter and hash them for a key to your result index. Examples where I've used this: Caching of regex results, replacing file reads with modification date polls and read from cache if not changed..
Post reply on HN