Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Clojure's STM has several advantages going for it:

* Clojure's data structures are purely functional, and built with the STM in mind. Updates and rollbacks to structures are cheap.

* Clojure user-level code has always had access to the STM. Because that's the accepted paradigm, There are an order of magnitude fewer assignments in typical clojure code, greatly simplifying the problem. In typical clojure, one function that uses the STM will call 10 pure functions. In typical python, most of those 10 calls will all mutate local state.

* Clojure requires all STM activity to happen within the dosync block. In python, that would be like requiring the magic to happen within a "with transaction(): ...". Again, this limits the amount of work that needs to happen. The compiler doesn't need to decide whether or not a random function needs to be a transaction; the user will tell the compiler.

* Clojure's STM explicitly only works with functional code. This will likely cause fewer leaky abstractions going forward. PyPy will have to modify all existing data structure classes to work with the new STM, and they won't be able to fix all the library and user code out in the world.

Good luck to them, but I suspect bolting STM into an existing language is much harder than designing it from scratch, similar to building in GC.



How does Clojure enforce that there are no side-effects in the transactional code? Is there a macro that "compiles" the transaction somehow?

STM in Haskell is very natural, since Haskell already enforces pure code and side-effecting code. STM and STVar work pretty similarly to IO and IORef or ST and TVar.


    How does Clojure enforce that there are no side-effects in the transactional code?
Technically it doesn't. If you have a (println "foo") in a transaction it may be printed multiple times if the transaction gets retried.

Clojure does give you an io! macro that you can wrap all your side effects in to manually provide that kind of protection. If you try to use io! inside a transaction you'll get an error at runtime.

    user=> (defn foo [n]
             (io!
               (println n))
             n)
    #'user/foo
    user=> (defn bar [] (dosync (foo 10)))
    #'user/bar
    user=> (bar)
    java.lang.IllegalStateException: I/O in transaction (NO_SOURCE_FILE:0)


It doesn't. What I meant by my last statement is that the transaction API forces updates to be functions of the old state, a somewhat foreign concept in python.


I think you've misunderstood what they're proposing here. Especially this:

> Clojure requires all STM activity to happen within the dosync block. In python, that would be like requiring the magic to happen within a "with transaction(): ...".

Under the proposal, all Python code would run under STM, only C-API and OS-level codes would run outside STM.

Edit: to clarify, the most common issue with implementing a viable STM (in just about every language apart from haskell) is the interaction between STM-managed objects and non-stm-managed objects. Under Pypy's proposal, there would be no such thing as all objects would be STM-managed.


I do understand that aspect of their proposal. My point is that causes unnecessary amounts of work. In typical Clojure, there's a very high ratio of pure functional code to STM code. i.e. you'll probably make 10 or 100 pure functional calls between each STM interaction.

Under the current pypy proposal, they're going to STM-ize every function call, regardless of whether it needs to do STM work. If the language had been built with STM in mind, with an explicit STM call, more user and library level code would adapt to that, and use STM sparingly.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: