> > The fact that `function2` is logging stuff is an implementation detail that callers shouldn't care about.
> On the contrary, I think it definitely matters. If a function is going to log something, I want to know about it.
You are missing the forest for the trees.
First of all, why you'd care that a function you're calling is logging stuff is a bit beyond me but fine. Think of something else. Maybe the function is calling memcache, or storing stuff in the database, or sending a UDP packet to a message bus, or is querying the location service. Surely you can agree that there are things this function does that you don't care about if all you need is an Account given a user id, right?
These things you don't care about are called implementation details. Callers shouldn't know about them, therefore they shouldn't have to pass them in parameters.
That's what dependency injection (injection, not passing) does for you. It lets you call
val account = getAccount(userId)
instead of
val account = getAccount(userId, logger, memCache, db, messageBus)
The first example is using dependency injection and correctly hides the implementation details of `getAccount` while not being referentially transparent.
The second example is referentially transparent but exposes all kinds of private implementation details, making the callers' life very difficult, if not impossible (how are they supposed to come up with a messageBus when all they have is a user id?).