Live data from Hacker News

Modern Functional Programming: The Onion Architecture

degoes.net

51–60 of 100 posts

Re: Modern Functional Programming: The Onion Architecture

#51

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 understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't. If you want to stick to referential transparency, you can't use dependency injection: you have to pass all the parameters the function needs. None of these can be implicit or belong to a field on the class sin…

> And because of that, functional code is often very tedious to test. Actually, in my experience, there is a clear tension between code that's referentially transparent and code that's easily testable. In practice, you have to pick one, you can't have both.

In all my years of software development, I've never encountered a referentially-transparent function that was even remotely hard to test, let alone harder than one with environmental baggage. In fact, being referentially transparent opens you up to new kinds of powerful testing strategies that are nearly impossible if the function isn't, like QuickCheck. (I can't highly recommend quick check enough, it's worth the little learning curve 100x over)

Re: Modern Functional Programming: The Onion Architecture

#52

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.

If `function2` needs to log something, then it should have a type signature which reflects that.

Logging is a side-effect. Logging requires configuration to be passed in; it means having access to some file descriptor or other object to interact with, it could potentially fail to connect, or cause a computation to hang, or cause a service to trigger, or make a disk run out of space, etc. If a function wants to log something it's not a simple reader anymore but something more complex. The fact that in Haskell this is reflected in the type signature of the function is again a good thing. It's not "polluting" the method signature; it's putting more information in the method signature. Not letting you hide side effects in a computation that appears to have no externalities is a strength of Haskell, not a weakness.

Re: Modern Functional Programming: The Onion Architecture

#53
post #50
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…

The graphics are both indeed circular which makes them look alike, but the meaning is completely different. In the OO graphic, it's representing a single cell organism, hence the circle. This is not the complete application. It's encapsulating the state of a single process, and only through externally interacting with the organism can the state be inspected & changed, all based on time. The circles in the FP represen…

Them being circular has nothing to do with what are they representing. They are showing off the idea of hiding implementation through abstracting it. This is the exact same concept that both of these photos are showing.

Re: Modern Functional Programming: The Onion Architecture

#54
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.

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.

Re: Modern Functional Programming: The Onion Architecture

#55

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.

Given that you'll notice whether or not function2 does any logging, it's definitely not an implementation detail.

Re: Modern Functional Programming: The Onion Architecture

#56

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 understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't. If you want to stick to referential transparency, you can't use dependency injection: you have to pass all the parameters the function needs. None of these can be implicit or belong to a field on the class sin…

> I understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't.

> And because of that, functional code is often very tedious to test.

Your argument rests on a fundamentally wrong assumption. Expressions in functional programs do not have to be (and indeed are almost never) referentially transparent. Just consider global or module-level immutable variables. Those function names? Also not referentially transparent. This goes all the way back to free variables in the lambda calculus: https://en.wikipedia.org/wiki/Lambda_calculus#Free_variables

Further, dependency injection is a completely idiotic and broken pattern and IMO the worst thing to come out of object oriented programming. Once you have dynamic scoping (surprise! also not referentially transparent) everything that DI does (and much more) becomes trivial.

Re: Modern Functional Programming: The Onion Architecture

#57
post #4
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…

The difference is that a "layer interpreter" in functional style is just a pure function that transforms values to values, so it's very easy to test in a very direct way. And we have laws that guarantee that composition works correctly (i.e. the interpretation of a composition is the composition of the interpretations). Whereas it's a lot fiddlier to confirm that an object that uses other objects behaves correctly (y…

Thank you for writing 'functional style' and not 'functional language'. While the latter is usually, and quite naturally, better at expressing the former, there is usually a lot to gain simply by adapting the style to the issue at hand, which not _necessarily_ implies changing languages.

What follows is not necessarily of high value, I'm simply a working programmer since 20 odd years that's bit weary and sad that the craft appears to be stuck in a rut by getting stuck between an unnecessarily theory-less reality and a nirvana of unrealistic purity. It's - maybe - a backdrop to explain why I felt a need to thank you for your choice of words with so many words.

If we consider every problem has a shape (loose analogue for the set of constraints thet uniquely identifies a problem) , then for every shape, or class of shapes a problem embodies, there exists an in some sense - ideal - language to solve that problem. Few are however the times when you only need to solve one discrete class of problem in the same system, but in case of mismatc between language and shape, it's quite common the only solution brought forth is to change languages. Unfortunately that solution is rarely feasible for a multitude of reasons. In the fallout after having to keep working with the same non-ideal language, the entire idea that there are multiple ways - styles - to express a solution is sadly often lost. This would not necessarily happen if the idea that style matters enough that when we can't change language, we could, and would still change how we express the solution within our constraintd. Be it in any language or paradigm under the sun, we need the words from them all to be able to talk about our problems, as they are either unique or already solved.

Re: Modern Functional Programming: The Onion Architecture

#58
post #56

Earlier quoted context omitted.

I understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't. If you want to stick to referential transparency, you can't use dependency injection: you have to pass all the parameters the function needs. None of these can be implicit or belong to a field on the class sin…

> I understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't. > And because of that, functional code is often very tedious to test. Your argument rests on a fundamentally wrong assumption. Expressions in functional programs do not have to be (and indeed are almost neve…

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

Re: Modern Functional Programming: The Onion Architecture

#59
post #19
post #14

Earlier quoted context omitted.

Yeah, the free monad structures involve lots of lambdas. It's not so much that information is "lost", more that you cannot see beyond the next lambda abstraction. From a DSL perspective, it's like you can only inspect the program so far as to know the next statement in the "do" block. To see what the next statement will be, you need to actually evaluate the current one. Instead of free monads, if you want very analyz…

The problem is that applicative functors aren't powerful enough to allow computations that depend on previous results. I suspect what we want is something like free ArrowChoice, but I'm not aware of any work in that direction.

AplicativeDo solves this a little bit through using Applicative until the results are necessary for the next instruction, then opting for Monad instances

Re: Modern Functional Programming: The Onion Architecture

#60
post #56

Earlier quoted context omitted.

> I understand the value of referential transparency and how it makes "certain" things easy, but saying that it automatically makes testing functional code easy is a myth. Sometimes it does, sometimes it doesn't. > And because of that, functional code is often very tedious to test. Your argument rests on a fundamentally wrong assumption. Expressions in functional programs do not have to be (and indeed are almost neve…

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?

Post reply on HN