Comments (3)
Oooh this is interesting. I will think about the concurrency part itself a bit more before commenting.
But I am thinking forcing the promise for value shouldn't be done through invokable just because it might be confusing. We could use the Clojure approach may be? (deliver promise)
or @promise
from parens.
But I am thinking forcing the promise for value shouldn't be done through invokable just because it might be confusing
Agreed.
We could use the Clojure approach may be? (deliver promise) or @promise
How about a method on promise? (.resolve p)
, or something similar?
One of my major gripes with Clojure that the default namespace too cluttered. I'd rather not add any globals if we can avoid it. The @
notation is alright, but I'm also wary of introducing too much special notation, lest our lisp engine devolve into an outright lisp implementation.
I will think about the concurrency part itself a bit more before commenting.
👍 Yeah, I'm still mulling this over on my end as well. I agree we should take the time to do it right.
Addendum:
Whatever solution we go for, it should also support a SelectExpr
!
from parens.
Just quick brainstorm ... I'm really not sure if the following is a good idea... 😅
Nevertheless, #17 introduces the notion of a goroutine into Parens via the GoExpr
type, and we still need to determine how goroutine management and synchronization should be done in Parens-flavored lisps. The simplest and most straightforward approach resembles native Go: don't help the user with anything. Memory is shared, race conditions are possible, goroutines can leak, synchronization must be explicit, and the runtime assumes nothing.
But it occurs to me that Lisp is not Go, and that there might be an opportunity for a bit of constructive hand-holding ... maybe.
Let us start with the observation that our GoExpr
semantics differ slightly from the semantics of the native go
statement. Whereas go func() { ... }
represents calling a function in a separate thread of execution, GoExpr
represents evaluating an expression in a separate thread. This distinction is largely meaningless, save for one fact: all lisp expressions return exactly one value. All go functions do not. They may return an arbitrary number of values, including zero.
Why does this matter? Because some bare-bones synchronization might be relatively easy when we don't have to handle arbitrary arities. It should be pretty easy to pass that return value back to the calling thread. Right now GoExpr
throws away the result of evaluation:
func (ge GoExpr) Eval(ctx *Context) (value.Any, error) {
child := ctx.fork()
go func() {
_, _ = child.Eval(ge.Value)
}()
return nil, nil
}
This means that if we want to recover the a value from a separate goroutine, we have to wrap the expression of interest in e.g. a second function invocation:
;; pseudocode
(let [ch make-chan]
(go (my-expensive-func ch))
(print (<- ch)))
But what if we allow users to recover the value of the expression they've evaluated in a separate thread? We could make GoExpr
return a Promise
type, which is invokable, and which blocks until a response is available when invoked.
type Promise struct{
// both channels are buffered (len=1)
val chan value.Any
err chan error
}
(p Promise) Eval(ctx *Context) (value.Any, err) { return p, nil }
(p Promise) Invoke(ctx *Context, args ...value.Any) (val value.Any, err error) {
select {
case val = <-p.val
case err = <-p.err
}
return
}
GoExpr
could then be rewritten thusly:
type GoExpr struct {
Value value.Any
}
// Eval forks the given context to get a child context and launches goroutine
// with the child context to evaluate the Value.
func (ge GoExpr) Eval(ctx *Context) (value.Any, error) {
child := ctx.fork()
p := Promise{
val: make(chan value.Any, 1)
err: make(chan error, 1)
}
go func() {
// N.B.: we don't need to close any of the channels (see note below).
if val, err := child.Eval(ge.Value); err != nil {
p.err <- err // non-blocking due to buffer
} else {
p.val <- val
}
}()
return nil, nil
}
The initial lisp code can now be rewritten as:
(print ((go my-expensive-func)))
A quick note on performance. Note that because we never have to close the channel, we can recycle these using a clever mix of runtime.SetFinalizer
and sync.Pool
.
Just to reiterate, I'm not sure this is a good idea. My gut says that Go's concurrency semantics won't map onto a functional language perfectly, and that we'll have to find some new ways of expressing familiar concepts like go
or <-ch
. On the other hand, I'm not 100% sold on this particular approach. Moreover, I'm very much attached to Go's "hands-off" approach to concurrency, so all of these things need to be weighed against each other.
In any case, this has been nagging me for a few days, so I wanted to get your thoughts. 🙂
from parens.
Related Issues (9)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from parens.