4 ms·
I disagree with some of the sentiment expressed in the article; mostly functional programming works much better than profoundly non-functional code, and more fu
by Kutta 12y ago
I disagree with some of the sentiment expressed in the article; mostly functional programming works much better than profoundly non-functional code, and more functional programming usually delivers marginal benefits.
The fact that effects can be used to simulate other effects does not imply that programmers would typically try doing that. In fact, programming style is more often shaped by trivial inconveniences (often syntactic, or the availability of certain libraries) than most of us care to admit. A programming language that merely discourages people from writing spaghetti code should still cause less spaghetti code being written. Freak bugs will pop up from time to time, but not as often.
Shifting an existing language to a more functional style should be a net negative thing only if the resulting extra complexity and bloat gets excessive. Starting from an OOP/imperative position, it gets harder and harder to support an additional (marginal) piece of FP functionality, and after a while it's just better to start over and switch to pure FP, but current OOP languages flirting with FP are not particularly close to that point. They haven't started in earnest to introduce purity to an impure core language (which would be rather awkward, as the article points out). In this situation it is somewhat early to talk about mostly functional programming not working.
Scala might be closest to the point of excessive bloat, but I'm not really familiar with it, and I think that the FP support there is a good thing, especially in contrast to the Java code that it might displace.
(Disclaimer: I program chiefly in Haskell and I love it).
- tethis 12y agoAnd it's important to note with Scala, a big part of its complexity comes from the practical philosophy of its creators: purity is sacrificed in order to actually make it work on the JVM the way we want it to. I used to work with Java on the server-side, but I'm programming almost entirely in Scala now (I'm at a small shop where I was lucky enough to convince the boss to let me give it a go on a project last year) and I've got to say that it's completely changed the way I think about and solve problems. I learned Haskell in university and I'm trying to learn more, but I don't see it ever being accepted in our office. The Scala syntax does (especially when working with async programming / Futures) suffer from problems, like the nested callback problem that Javascript also has, but there is an elegant solution in the language... you just have to know how to use it. But on the other hand, it doesn't look like a completely foreign language to Java developers and it's not too hard to get our new hires productive with it.
- muhuk 12y agoHow do you manage nesting from callbacks and matches and such? Inlining short functions like _ + _ is fine, but how do you organize more advanced operations? (I'm just constantly looking for ways to make my scala code more accessible.)
- saryant 12y agoHe's probably referring to for-comprehensions.
- tethis 12y agoI am indeed referring to for-comprehensions. Any time you have nested flatMap/maps you can replace it with a for, since that's all a for-comprehension really is... computation1.flatMap { result1 => computation2.flatMap { result2 => computation3.map { result3 => result2 + result3 } } } Becomes... val foobar = for { result1 <- computation1 result2 <- computation2(result1) result3 <- computation3(result2) } yield { result2 + result3 } Or, since I'm using the async Postgres module which returns Futures, to make them run in parallel you need to create the futures beforehand. I often have something like this... val fProject = Project.findById(projectId) val fTask = Task.findById(taskId) for { projectOption <- fProject taskOption <- fTask students <- projectOption match { case Some(project) => Student.findByProject(projectId) case None => Future.successful(IndexedSeq[Student]()) } result <- (projectOption, taskOption) match { case (Some(project), Some(task)) => { /* do something with project, task and students */ } case (None, _) => Future.successful(NotFound(s"Project $projectId not found")) case (_, None) => Future.successful(NotFound(s"Task $taskId not found")) } } yield result By creating the futures first, they both run in parallel and their results are 'collected' by the for comprehension. In the first example, the computations necessarily run in sequence. I'm using Play framework, for me, these for comprehensions are usually found in my controllers and the final result is an HTTP result. Passing along failure can still be tricky but I find this much more organized than nesting callbacks.
- leoc 12y agoIt's an obviously silly exaggeration: what, did MacLISP, Scheme or Standard ML not exist? It's not as if Erik Meijer has the excuse of never having heard of them...