The Environment as a Value

461 views
Skip to first unread message

Mikera

unread,
Mar 29, 2013, 7:26:32 AM3/29/13
to cloju...@googlegroups.com
We all love immutability in Clojure.

So why not make Clojure environment (including namespaces etc.) into an immutable value?

Approach:
- Environment is a persistent data structure. Something like a map of maps (Environment->namespaces->vars)
- "def" becomes an operation that produces a new Environment
- In the REPL not much changes: you always work on the latest Environment.
- You can resolve symbols in an Environment, which becomes an explicit parameter rather than an implicit global state.
- Code can read *environment* to get the current Environment for a thread.
- With liberal use of *environment* as a default, code that doesn't rely on mutating the environment can (probably?) run unchanged.

Some potential advantages:
- Simpler? 
- Better aligned with Clojure's philosophy. Usual advantages of immutable values.
- Code can be "isolated" with different environments, e.g. different app server contexts.
- We can "snapshot" a running program Environment for later use / analysis
- Environment updates could be made transactional. Anyone have a use for this?
- Threads have their own environments (a better alternative to binding?)
- A secure sandbox can be implemented with a reduced Environment
- Test code can run in a mocked-out Environment
- Allows for roll-back of Environment state (useful at the REPL?)
- Environments are merge-able (basis for a module system with concurrent loading?)
- Lazy/concurrent compilation probably becomes feasible (a big boost to Clojure startup time?)
- Probably simplifies issues with code reloading
- Probably simplifies static analysis / type checking

Some downsides:
- A fair bit of work, obviously.
- Doesn't change the fact that you can store mutable things in the environment (atoms etc.)
- New semantics for lots of things. Though might be possible to coexist with / emulate old semantics.
- Almost certainly breaks stuff
- Represents major language design change - Clojure 2.0 territory at least

Thoughts?

Is this a possible extension to Clojure, or is this a completely new language?


Laurent PETIT

unread,
Mar 29, 2013, 11:12:17 AM3/29/13
to cloju...@googlegroups.com
As far as I have understood the slides,

Clojurescript in Clojurescript already uses "environment as a value".

HTH,

-- Laurent


2013/3/29 Mikera <mike_r_...@yahoo.co.uk>


--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Gary Trakhman

unread,
Mar 29, 2013, 11:19:56 AM3/29/13
to cloju...@googlegroups.com
Maybe we should implement clojure on top of datomic, jk.

I think there's a simple/hard problem with this, the notion of a
dynamic environment necessary implies global state, and that's
complected with jvm memory state.

How would you reconcile this?

Env a:
(def a (atom b))
(def b something-else)

Env b, like 'a' except:
(def b something-else2)

now the value of @a is shared between two dynamic environments, and
they are coupled statefully.

I think it's better just to avoid using dynamic scope for this kind of thing.

You could just always code using only lexical scope, but you lose the
dynamic/global advantages of vars.

;; an example of that program:
(let [...] (let [...] ...))

The lexical scope is then your environment.

Mikera

unread,
Mar 30, 2013, 12:15:35 AM3/30/13
to cloju...@googlegroups.com
On Friday, 29 March 2013 23:19:56 UTC+8, Gary Trakhman wrote:
Maybe we should implement clojure on top of datomic, jk.

Not such a crazy idea as it sounds: I've got a back-of-the-envelope design for a persistent BPM system that does exactly that :-)
 

I think there's a simple/hard problem with this, the notion of a
dynamic environment necessary implies global state, and that's
complected with jvm memory state.

How would you reconcile this?

Env a:
(def a (atom b))
(def b something-else)

Env b, like 'a' except:
(def b something-else2)

This is an interesting case.

Under Clojure's current semantics (eager compilation) then it's obviously an error because b isn't defined at the time you define a.

If you swap the original declaration order of a and b (maybe what you meant?) then yes, you would get two Environments sharing the same atom (with the value of b from the first environment when a was defined). This might be a problem, or it might equally be exactly what you want... having distinct Environments doesn't change the fact that you will sometimes want to share mutable state.

If you opt for the "lazy compilation semantics" then (def a (atom b)) would work fine but leave an unresolved dependency on b. Then when you (re)define b, this dependency would be resolvable. Then when you force compilation (e.g. by reading @a) it would compile and execute (atom b) which would in turn force the value of b. So you *could* end up with two different atoms in the two different Environments - it depends on exactly how you define the lazy compilation semantics.

The last case opens an interesting possibility that redefining b could always force re-evaluation of (atom b) via setting up a dependency relation, so you would get a new atom whenever you fork the environment by redefining b. I'm still not sure whether this is a good idea or not, but it sounds like a potentially useful feature if you needed to re-instantiate different Environment versions (app server contexts, test cases setup etc.)
 
Reply all
Reply to author
Forward
0 new messages