Hacker News new | ask | show | jobs
by jolmg 2100 days ago
> I don’t see how this is functionally different to passing an object that contains references to the relate to variables; which I’ll call a context object.

It's the same difference as using environment variables vs command line arguments. Imagine all programs having to pass TERM, DISPLAY, HOME, etc. as arguments in case some descendant process wants to use it and the user override. Like passing TERM, DISPLAY to git in case you want to override them for the configured pager or editor.

In other words, the issue is that when you have project A using project B using project C, project A has to manually carry around the context of B, and C in case the user wants to override them.

2 comments

> Imagine all programs having to pass TERM, DISPLAY, HOME, etc. as arguments in case some descendant process wants to use it and the user override. Like passing TERM, DISPLAY to git in case you want to override them for the configured pager or editor.

Interestingly this is exactly what I have been wishing was standard practice for a few years now! Otherwise you end up pickup up implicit configuration from the environment that you didn't intend at all.

> In other words, the issue is that when you have project A using project B using project C, project A has to manually carry around the context of B, and C in case the user wants to override them.

Yes please! That makes the most sense to me. I admit my years of experience with Haskell may have coloured this opinion.

> Interestingly this is exactly what I have been wishing was standard practice for a few years now!

You mean that when you call e.g. `tmux` you want to be forced to call it as something like `tmux -e HOME=... -e TERM=... -e DISPLAY=... -e USER=... ...`, and likewise for basically all other programs?

> I admit my years of experience with Haskell may have coloured this opinion.

Reader in Haskell helps prevent having to do that similar to environment variables or dynamic scoping, and is really, really common to use it as such instead of having to pass arguments around, so...

> Reader in Haskell helps prevent having to do that similar to environment variables or dynamic scoping

Reader in Haskell addresses some of the same concerns, but it differs in important ways. Reader is statically determined, Reader is clearly scoped to particular parts of your program (you can see whether a given function's behavior might depend on a value from a Reader in a way that's not visible with dynamic scope or environment variables), and Reader forces you to have set everything you might access. You could work around that last by making it a `Reader (Map String Dynamic)` or something, but that's not common in Haskell.

I make no claim, here, that these are obviously the right decisions - but they seem to provide enough differentiation that someone could rationally prefer one or the other.

On the command line, i.e. a human interface, I'd be willing to soften my stance a bit but any time a program calls another program, yes, I would like it to be explicit about the parameters it accepts.

> Reader in Haskell helps prevent having to do that similar to environment variables or dynamic scoping

I would be happy with an interface like Reader in Haskell but I don't see that it's much like dynamic scope. The subject has been explored a few times in this discussion.

This is the best argument I've read for dynamic scoping thus far.
Isn't it also the worst argument :-) In that it is well explained but I look a little bit in horror on it.

For me it seems like dynamic scoping is very similar or at least related to dependency injection. Which seems to be one of those things that really have polarized opinions of developers. Some people love it because the code looks really neat and it is really easy to add new pieces. Other hates it because it is really hard to understand what is happening under the hood.

I would say that dynamic scoping is a bit worse (in my opinion) in that it is like dependency injection that are supposed to be modified one or more times in between so it will be almost impossible to figure out why a value is what it is when it goes wrong.

Incidentally that makes it super easy to mock out or intercept subsystems if they use dynamically scoped hooks e.g. in most languages intercepting stdio is a pain in the ass (you might have to swap out multiple globals, it's not thread-safe, etc…).

If you're in CL however, you can just rebind standard-output and you'll capture what anyone downstack sends there.

Or rebind standard-input and feed an interactive program input from a file or other stream.