... having channels doesn't have anything to do with Reactive Programming (http://en.wikipedia.org/wiki/Reactive_programming). The implication that channels were added to Go to support it seems dishonest.
There is a very long thread on the Go list of a guy who is a fan of dataflow grumbling about how design decisions made in Go make it a poor fit for dataflow.
Sadly, 'reactive' -- like OOP -- seems to be one of those words that gets reinterpreted to meaninglessness. The definition I'm most familiar with, from the Reactive Manifesto,[0] seems like it would fit Go's channels just fine... but anything that puts Go and Erlang and Node.js in the same box might be too broad to be useful.
Maybe I'm missing something but that manifesto doesn't seem to have anything at all to do with reactive programming, except that maybe you could use reactive programming to help build "reactive applications".
> having channels doesn't have anything to do with Reactive Programming
Mostly agree, in the sens that, from my limited pratical experience & knowledge of Reactive Programming (RP), I understand that RP isn't bound to a particular "parallel execution model" (maybe s/parallel execution model/threading model/ ?).
> There is a very long thread on the Go list of a guy who is a fan of dataflow grumbling about how design decisions made in Go make it a poor fit for dataflow.
A blog post might be better fit for that (so far I was unsuccessful getting constructive feedback that way). Anyway here are more thought, based on my experience dealing with GUI app:
- "parallelism", even if simpler with an event-loop, is still a significant burden especially when performance matters
- Class based OOP, especially not JavaScript, is very helpful to group a sequence of callbacks to help fight spaghetti code.
- Explicit State Machines are a praised black art wrongfully dubbed useless overhead because "it's possible to do otherwise"
All the points mentionned above have in common that they try to handle correctly with fine grained priority and different "paralellism" patterns with ease.
RP mean to provide much more than "yield", it's pattern that is similar to the explicit state machine intent. The declarative paradigm/syntax arguably provides an ease of use that is competitive both in terms of performance and dev efforts even if mileage may vary. See by yourself:
It’s funny how similar the arguments against OOP and
Reactive Programming are.
It's funny how similar this argument in favor of Reactive programming is to the arguments used in favor of other purported paradigm shifting trends that never went anywhere. Aspect Oriented Programming anyone?
I'm also not convinced that the arguments really are the same. The biggest complaint I've heard about OOP isn't that "you can get by without it," as the author claims. Rather, it's two things: Proliferation of objects such that the codebase can't be understood holistically, and the dangers arising from objects having internal state. I've never heard either of those arguments against reactive programming.
The most common complaint I've heard about reactive programming is that it has yet to be realized in the form of mature libraries. (In general, for most languages. I'm sure at least one language has it.) Thus, whether it's a great idea or not is largely immaterial for most workaday developers, who won't be able to use it until the ecosystem exists.
Can someone tell me the key differences between the actor model and reactive/dataflow programming? I haven't seen any references to erlang in conversations about Reactive programming, which seems odd to me as erlang's lack of shared state and message-passing seem like they fit the definition of reactive programming.
As I understand it (and I may not understand it at all!), the most significant difference is that dataflow programming is deterministic and compositional, whereas actors are nondeterministic and discrete. Intuitively, I tend to compartmentalize dataflow along with maps, filters and comprehensions; and actors with FSMs. This is probably an incorrect generalization, but it works for me.
Somewhere, Hoare gives an example of pipelining the sieve of Eratosthenes through a series of communicating processes, which is a good example of dataflow programming -- you send a list of integers to a process that sieves out even numbers and sends the result to a process that sieves out multiples of 3, and so on. Such a pipeline is deterministic -- given a set of values, you will always get the same output -- and compositional -- the sum of the sieves is greater than the individual parts.
A traditional actor-based model, on the other hand, isn't well-suited for that kind of problem, but is great if your goal is to dispense biscuits and chocolates. State is kept locally, and the actor can change its behavior to external messages based on internal state -- that is to say, actors have discrete identities. The response to any particular message is nondeterministic, because it depends on what's the actor's internal state and decision process may be, and you can't really combine multiple vending machines to compose a new, super-vendor.
I think the key difference is in which side of the conversation between transmitter and receiver the intentionality is vested.
- Actors have no control over where messages come from. Actors have complete control over what other actors receive their messages.
- In a reactive model, a producer sends messages blindly, with no control over the receiver. The consumer chooses its sources specifically.
I'm not sure if this is exactly the right answer, but it is the impression I got after reading a paper on dataflow programming [1]. I'd be interested in whether anyone has any deep insights on the relative advantages of each model, and whether they're actually equivalent on some level.
They're basically unrelated. The actor paradigm is basically imperative programming, with boundaries being put up between the actors, and the way you cross the boundary is with a message. If that sounds too foreign, bear in mind that while OO doesn't have to be an "Actor" and an actor doesn't have to be "OO", they share a certain structural similarity, in that one of the primary characteristics of working with an "object oriented" system is that you are creating imperative islands of code, and the way you cross the barriers are via method calls. Indeed back when OO was first being formulated you can see the terminology was not settled yet, and you'll see method calls being called "messages" even today in some OO communities.
The Actor model is basically OO, except that objects are given their own execution contexts, whereas in OO there's one execution context that drives the objects. You can do worse for your first design in Erlang than to break your task into core objects exactly as you would for a high-level OO design (don't put in all the little collection and iterator and other structure classes, stick to the "high level" design), and use that to drive your process design. It won't be perfect, but it's not a bad first pass. The profound differences between one execution context and multiple also has subsequent follow-on effects on the languages in question, and in what direction they evolve, so in practice they feel quite different despite the underlying similarities in the model.
Reactive programming is extremely different. There, values are defined as functions on other values, intrinsically, such that if A is B + C, future changes to B and C mean that A is automatically updated to the new B + C with no programmer interaction. This is no longer imperative design; instead of your program consisting of a huge set of instructions for the processor to directly execute (no matter how gussied up that is by the syntax and semantics of the local language), your program consists of a declaration of relationships between values and how they evolve over time. The fundamental operation of the programming language is no longer "Set value identified by X to Y", but "Attach value X to value Y with update function Z".
As is always the case in the world of Turing Completeness, this can be embedded into OO, or OO can be embedded into it, etc. all sorts of combinations are possible. But it is a fundamentally different underlying method for writing your program.
I would observe that A: this isn't new, which I say not because anyone is claiming that it is but because it means that we can in fact draw on past experience to examine it and B: our general experience with it up to this point is that it often makes small tasks easier, even much easier, but that trying to create whole systems this way is often quite problematic. If you think laziness in Haskell is hard to understand, just wait until you're trying to understand a cyclic dependency graph in a data-binding environment (the usual OO manifestation) or a spreadsheet, with the local bizarre adaptations to try to make that sensible all interacting with each other in crazy complicated ways. It's a useful tool, and I don't mean that as a mere rhetorical sop, it really is a useful tool to have in the belt, but I'm deeply skeptical of it as a proclaimed savior of programming, or the Next Big Paradigm. Our experiences with it up to this point have not been positive enough to justify that claim, IMHO, and I haven't seen that there's been any sort of big enough change in the landscape lately to justify that.
Compare to "functional programming", which has been the Next Big Paradigm for a few decades now, but IMHO modern Haskell of the past 3-5 years really is a big enough advance to justify some examination on the topic. (I'm not saying Haskell is guaranteed to be the Next Big Thing, I'm just saying that there's been enough action there to legitimately justify having a fresh look at the situation.)
If you can point me at the big advance for reactive programming in question, I'd love to have a look. I know the Haskell world has been fiddling with FRP but I'm still underwhelmed by the efforts.
> just wait until you're trying to understand a cyclic dependency graph in a data-binding environment (the usual OO manifestation) or a spreadsheet, with the local bizarre adaptations to try to make that sensible all interacting with each other in crazy complicated ways.
I think that may be a significant, fundamental problem with reactive programming. The way these graphs are constructed is typically (necessarily?) buried within the reactive programming framework, giving the programmer poor visibility into what's actually happening. Maybe that problem can be solved, but I suspect it might be intrinsic to reactive programming.
> I know the Haskell world has been fiddling with FRP but I'm still underwhelmed by the efforts.
I've test-driven some of those projects. Last I checked, the documentation was extremely sparse, and the only examples were trivial. I haven't yet seen an example of a useful Haskell app built with FRP. FRP has sometimes been described as the silver bullet for doing stateful apps in a pure FP way. Maybe it is, but for now, I'm sticking with pseudo-imperative Haskell for those purposes.
> Now OOP is taken for granted and assumed that all languages should be able to produce objects.
Not really. There are OOP languages, there are multi-paradigm languages that include OOP stuff, and then there are languages that don't have OOP at all. That last category includes functional languages like Haskell and some Lisps.
Should every language support at least some OOP? Maybe. I think it's too soon to tell. We may eventually learn that pure functional languages are better for maintaining a sane codebase, in which case OOP will not be considered a mandatory language feature.
What Haskell lacks is hierarchies of subtyping. It certainly can be used for OOP. I've written object-oriented code in Haskell. It's not common, but it's not hard. And on rare occasions it's the best solution to a problem.
Haskell does not have OOP facilities built into the language. But then, Haskell arguably doesn't have sequencing of IO built into the language. Standard libraries get the latter done, in many ways better than it's done in other languages. The former lacks a canonical representation provided by any standard (or nearly standard) library I'm aware of, but it's pretty easy to implement the pieces you need yourself once you're aware of the patterns.
Perhaps we have different definitions of OOP. To me, it's not OOP without subclasses and private mutable state. What does OOP mean to you? I'm guessing you're thinking of Haskell's typeclasses as an analogue to Java's interfaces. Any other features?
You can implement private mutable state (and dynamic polymorphism behind an interface) with records of closures. Depending on what you mean by "subclasses" here, they may follow trivially. Haskell typeclasses are a poor match to OOP, as you say.
Some examples. The first is a discussion on it. The second and third have examples in scheme. I'd type more but I'm on my iPad. If I get time later I'll steal time on someone's computer to type up more.
Jtsummers gave some links, but in the interest of having something here:
data Surface = Surface -- presumably provided by drawing backend
data Shape = Shape
{ shapeGetCenter :: (Int, Int)
, shapeSetCenter :: Int -> Int -> Shape
, shapeDraw :: Surface -> IO ()
, shapeScale :: Double -> Shape
, shapeGetAABB :: (Int, Int, Int, Int)
, shapeShow :: String
}
instance Show Shape where
show = shapeShow
circle r x y = Shape
{ shapeGetCenter = (x, y)
, shapeSetCenter = circle r
, shapeDraw = undefined -- draw shape to surface
, shapeScale = \ f -> circle (f * r) x y
, shapeGetAABB =
( floor $ fromIntegral x - r/2
, floor $ fromIntegral y - r/2
, ceiling $ fromIntegral x + r/2
, ceiling $ fromIntegral y + r/2
)
, shapeShow = unwords [ "circle", show r, show x, show y ]
}
box w h theta x y = Shape
{ shapeGetCenter = (x, y)
, shapeSetCenter = box w h theta
, shapeDraw = undefined -- draw shape to surface
, shapeScale = \ f -> box (w * f) (h * f) theta x y
, shapeGetAABB =
let w' = h * abs (sin theta)
+ w * abs (cos theta)
h' = w * abs (sin theta)
+ h * abs (cos theta)
in ( floor $ fromIntegral x - w'/2
, floor $ fromIntegral y - h'/2
, ceiling $ fromIntegral x + w'/2
, ceiling $ fromIntegral y + h'/2
)
, shapeShow = unwords [ "box", show w, show theta, show x, show y ]
}
What's that old chestnut about design patterns just being workarounds for missing language features?
Multi-paradigm languages: I'll admit they're usually woefully lacking in sex appeal. When it comes to getting @$#@% done and moving on with your life, though, they be awesome.
There is a difference between "this isn't provided by the language, but I can easily implement it" and "this is impossible (or absurdly messy) in the language, but these work-arounds can make me care less".
There is a very long thread on the Go list of a guy who is a fan of dataflow grumbling about how design decisions made in Go make it a poor fit for dataflow.