As the author says, there's quite a lot of imperative stuff in this design. I'm still on the fence about whether that's the best way to build large applications with GUIs, network access, etc.
With the imperative approach, you make liberal use of the IO monad. There's a lot of explicit reading from and writing to mutable storage. If you're doing this approach right, you mostly just use the IO monad for actual IO and threading; most of the real logic of the program is kept pure. This approach has the advantage of being familiar. And more importantly, it just plain works. It's how I tend to do things.
Yet I can't help but wonder if there's a better way. The above approach, while comfortable, leaves me with a bad taste in my mouth, like I'm somehow fighting against the language. I've often heard functional reactive programming (FRP) described as the best alternative. Though I remain skeptical. I'd be interested in opinions about the future of FRP.
Seems like FRP is catching on in other languages (as steveklabnik's sibling comment mentions, Javascript, and in C# and Objective C) but in Haskell it still seems to be a wash; there are a bunch of competing frameworks and, as far as I can tell, no clear leader. I used reactive-banana to do a school project last year, and it was ok but not on the level of Rx or ReactiveCocoa as far as I can tell. There are a half-dozen or so other frameworks, and some of them are implemented differently from others as the research moves in different directions (different ways of representing events/signals, using arrows, etc). Of course, for this specific use case (GUIs) there are similar issues in terms of implementations of GUI toolkits in Haskell (GTK? wx? some other half baked ones? and so on)
But back to your other point, sure you can write imperatively in Haskell and use IO and so on, but to really take advantage of the strengths of Haskell you need to write declaratively (i.e. functionally) and take advantage of the ways that helps you write things like GUIs (which are naturally declarative).
I think FRP is a good idea; but it seems to expose a failing of many languages: the lack of metaprogrammability. In particular, most (non-Lisp) languages don't let you mess with control flow much.
The result is that when writing FRP code, users have to manage two control flows: the 'logical' control flow, which is expressed via whatever FRP library they're using, and the actual control flow, which is the one provided by the language they're in. Languages would need good support for source-to-source transformation, or, alternatively, allow the compiler IR to be manipulated by libraries.
From the little I've played with Haskell, it appears the monadic binding operator (>>=) lets you do this, and the user writes code in an idiomatic, imperative-like manner. It is extremely impressive that this capability can be used to arbitrarily extend the language, while being a very small concept.
Edit: not sure if I had my terms right on the operator.
Exactly! This is why I've turned my research away from FRP-style lifted values to just lifting all the code over time [1], so control flow is quite normal.
Yup! I would agree with this too. I've actually written an FRP library in Ruby[1] and it's not really _useful_ yet because of these kinds of issues. I was thinking about how I'd use it to build a web framework, for example, and I'm not sure it's a significant improvement.
Regarding >>=: yes, it's the 'bind' operator, and yes, it is really impressive. This is why monads are hard: they're so abstract that they're good for _many_ things. It's also why they're so great.
FRP strikes me as much, much closer to how Things Should Be(tm), but it still feels off somehow. Perhaps it's just too low-level. It should really part of a language's runtime, rather than exposed to the user. Right now, most FRP implementations kind of take over your system: you're always sweating whether this is a special reactive type vs your domain type. Even C#'s async/await (not quite the same thing as FRP) seems to have a similar effect, where the async function keyword propagates through your codebase quickly (caller/callee have to agree on this I believe).
FRP still feels like it's in the early stages, so I'm confident someone will find a better way to apply it. Right now it seems more palatable to non-bleeding-edge devs if you tailor it for specific use cases, rather than the whole thing at once. It may be the concept is too big to sell right now.
Are you referring to the various databinding frameworks? So far, I've been somewhat skeptical of those--I worry they make too many assumptions and take away too much fine-grained control.
The amount of people I know who use functional frameworks (say Underscore) to build things but don't really know much about functional programming is astonishing. To be fair, a lot of frameworks that espouse "functional programming" do so just because it's popular and really push imperative concepts instead so it's easy to be confused on the "right way".
I love Underscore (and Haskell), but I wouldn't consider Underscore an FRP framework at all. It's functional, yes, in the sense that it provides some of the most common FP operations like map, filter, etc, using lambdas. But I don't see the reactive part. Maybe there's some kind of reactive feature tucked away in Underscore's rather large feature set, but I haven't seen it, and it's certainly not core to the library.
We are able to subvert WPF databinding mechanisms to allow bindings to expressions as well as functions. The act of binding itself is still very imperative (and so, not very FRP-ish), but beyond that the programming styles are quite similar.
I dont think you need FRP to do that. In ReactJS, view code is pure, dom event callbacks just return new states[0], and all IO (ajax data access), async pipelines etc stay at the very top of your application at the root of the view hierarchy. Business logic is just functions of data values and/or appstate values. Which is exactly how I imagine the idiomatic haskell solution would look.
[0] dom callbacks actually swap! the nextState value so there are effects down there at the bottom of the view hierarchy, but that's react's fault for not letting you return the next state. e.g.
Interesting. ReactJS sounds like an immediate-mode UI that avoids states and reconstructs the UI by re-rendering. This is as opposed to a retained-mode UI that is based on a mutable scene graph.
That makes sense (can't read Chris's comment right now because I'm behind the GFW). Immediate mode is quite workable and can even be efficient with the appropriate amount of memoization. State can even be injected in a way that is very FRP like (re-render over values that changed). Seems like I have more related work to talk about :)
Not sure if "Architecture" is the right word here. More like, "How I built a Real World Haskell Application".
When I read about how to "architect" something I'm think about how to structure my Cabal file (a common pattern in my apps is a "library" of types, parsers, and utilities that are jointly used by multiple executables of the same program). I also think about structuring my tests, the naming and structure of module directory hierarchies, maybe also how to structure the program around the Main entry point.
I think there may also be a more interesting couple of "sub-articles" in this article: "How I built a server daemon in Haskell" and "How I built a GUI client in Haskell".
If architecture in the computing world consists of a system’s elements; the relationships between those elements; the attributes of the elements; and the attributes of the relationships, then architecture is not a timeline, process or stack description.
also: architecture implies that you had a variety of solutions to choose from, and that you made a choice from one of the possible solutions and are able to reason and talk about that choice, the pros and cons, etc.
If you can't list the pros and cons relative to the other choices, I don't really consider that architecture - that's just doing what's familiar.
An Architecture, to me, is an established and idiomatic process of applying solutions to produce a novel output.
In the case of a computer program the "architecture" should be extremely well-thought out, idiomatic, and using established practices to produce a program that is easy to maintain, understand, and test.
I don't believe you need diagrams but in the case of Haskell, the types (if you design type-first) serve as one piece of the blueprint that is to become your program, your cabal file, directory structure, documentation (both user docs and API docs), and tests are all part of that blueprint.
> it could benefit from the lens library, but last time I tried it did have problems with compilation with profiling information
In my experience, template haskell does not play well with profiling. If you were deriving lenses with TH, you could try writing them out yourself. That might solve this issue.
The industry appears to be very risk-averse (even though they have their reasons), so I'd be surprised to see a modern, functional language being used for critical tasks. However, C++ sometimes is used for those tasks, which boggles the mind.
I think you hit the nail on the head about the space industry being risk averse. I think that guides a lot of their decision making.
I took a compiler class that was taught by the inventor (Dr. Daniel Cooke) of another functional language, SequenceL (see http://en.wikipedia.org/wiki/SequenceL). Cooke had funding from NASA to develop SequenceL because evidently NASA was/is very interested in program correctness (it's hard to guarantee correctness of a C++ program) and a simplified programming model.
The way Cooke explained it in class, SequenceL was born out of a desire for a simpler programming language (he believed that modern programming languages are too complex - so many require extra tooling to be productive) and a desire to make the implementation of a program as close as possible to the definition of the problem. He reasoned that if you could make an executable specification (i.e. the definition of the problem was executable), then all the mistakes that implementers make while implementing a program based on the problem specification/requirements would go away. In other words, since the definition of the problem would be the implementation, zero implementation mistakes would be made.
Interestingly enough, the first SequenceL interpreter and compiler was written in Haskell.
> However, C++ sometimes is used for those tasks, which boggles the mind.
That's because the people making such decisions are not the ones qualified to do so (see the Lisping at JPL article you linked to). Their logic is probably along the lines of "if it's good enough for industry, ..." -- that's not an ellipsis, it is literally as far as the thought probably went.
I presume, "... and that's mission-critical in the same way that NASA's stuff is. Therefore, the idea that you can't/shouldn't do mission-critical software in C++ is mistaken."
Note well, though, that the coding standards strictly limit the C++ features you can use. It's not done the way your average bunch of C++ code cowboys sling out an application.
That doesn't negate what I was saying in the slightest: project managers are picking unsafe languages like C++ for safety critical applications for non-technical/ignorant reasons.
F-35 is a similar example (worse because the contractor was probably at fault here: using C++ made the software system go way over budget on a cost-plus contract, and will likely result in larger maintenance contracts down the road. That's a win-win as far as the contractor is concerned...)
I was under the impression that Ada is significantly safer than C++, particularly with the 2012 standard that adds support for contracts, and that it mainly suffers from a lack of tooling.
The elephant in the room there is the importance of tooling. Consider, explosions and explosive chemicals are themselves ridiculously dangerous and for the most part to be avoided. However, with the proper tooling and use, one can use them to accomplish some fairly amazing things.
That is, many of the "safer" languages try to get by with disallowing explosive and dangerous constructs. With some of the more advanced tooling that is available and good discipline, one can still do some amazing things with them.
My knowledge of safety critical software is not extensive and several years out of date, but my understanding is that the big two languages in safety critical software are still MISRA C (a subset of C designed to enforce safety via static analysis, see http://en.wikipedia.org/wiki/MISRA_C), and Ada.
As far as I'm aware, Fortran and Algol are not widely used. But feel free to correct me.
> Then again, that might just be a wrong impression I got from reading
I have a friend who worked on autopilot obstacle avoidance for airplanes and I believe they did indeed use C++. It does seem a little primitive / bug-prone compared to some more high-level langs.
Very cool to see Haskell being used more in the space space. I've recently built a command line tool in Haskell with a subset of these features for my team's testing:https://github.com/dangirsh/Auto
I agree that the existing tools are pretty limited
With the imperative approach, you make liberal use of the IO monad. There's a lot of explicit reading from and writing to mutable storage. If you're doing this approach right, you mostly just use the IO monad for actual IO and threading; most of the real logic of the program is kept pure. This approach has the advantage of being familiar. And more importantly, it just plain works. It's how I tend to do things.
Yet I can't help but wonder if there's a better way. The above approach, while comfortable, leaves me with a bad taste in my mouth, like I'm somehow fighting against the language. I've often heard functional reactive programming (FRP) described as the best alternative. Though I remain skeptical. I'd be interested in opinions about the future of FRP.