152 karma · joined September 25, 2009
The problem with your analogy is that the debris from the spaceship, etc. would still be visible to an observer after the fact. A better analogy would be this:
The aliens point a camera at our entire civilization. Ignoring the fact that this cloak works on the order of monumentally smaller scales than civilizations and the time it takes to put an ice chunk in a star, we use the cloak while we put an ice chunk in a star. The aliens would see our civilization jump from a state with no ice chunk in a star to one with an ice chunk in a star. Assuming that their measuring devices are up to the task, they would still be able to find all the spaceship debris, etc.
Monads are an example of using patterns from category theory in programming. This can be done nicely in languages with the facilities to do so, like Haskell. For instance, Functors, Monoids, and Comonads are patterns that are sometimes used in Haskell that are taken from category theory. However, the way monads are often used in Haskell is cargo cult mathematics. Here's why.
In category theory, monads are defined as functors with two associated natural transformations. However, in Haskell, the typeclass Monad is not even defined as a subclass of Functor. Additionally, one of the two natural transformations is swapped out for another (join is replaced by bind), and both are misleadingly renamed (return and bind don't suggest their actual meanings). Also, monads are often overkill for the problems they are applied to. Rather than think carefully about which pattern to apply to the problem, programmers echo the trumpeted “Monads are the fundamental method of abstraction!” and use them in their code, for that is the right thing to do.
Monads are not actions. Monads are not defined as a way to thread state through code. The type IO a in Haskell is an action, and since the interface provided to this abstract data type is monadic, there is a great deal of confusion about what the properties of a monad are and what the properties of the IO a type are. This blog post confuses them.
Even though monads are misused in this way in Haskell, their use still brings benefits. This is because Haskell gives the programmer facilities to write code that works over every monad. However, this is not the case in this blog post. The code in this blog post is parametrized over the types returned by the actions, but not over the actual type of action. One can see where he is looking at Haskell implementations of the functions to translate into C++ that they aren't. This basically renders the monads useless.
Another telling example of how these aren't really monads is when he says “You might have noticed that I use the words “action” and “program” interchangeably, although, strictly speaking, an action is the contents of a program. However, this distinction is an artifact or a Haskell quirk — a monad can’t be defined using a type alias, so we need the Prog type to encapsulate the action. Curiously, we won’t have this problem in C++.” In Haskell, an instance of Monad needs to be a container type, because the two functions that are the fundamental definition of a monad operate on nested containers (unit puts anything into a container, and join makes a container of containers into a single container). Since his C++ code isn't doing anything like this, he hasn't made a monad at all. He's simply made an extra layer of complexity in compiling an AST into actual code at compile-time, a task which has nothing to do with the nature of a monad. I'd reckon one could write code for compile-time EDSL's in C++ using no monads at all that would be much cleaner.
Sure, you can make a structure and slap the label monad on it and use return and bind functions (which aren't really what a monad fundamentally is) to put together your code, but if you can't write code that works as well on that monad as on other monads without changes, you've accomplished nothing but useless complexity. In other words, including program structures because they are accepted as good rather than because they have any benefit.
The author hasn't achieved the abstraction of a monad in C++ anyway; there's no way to write code that works on both this monad and others, which effectively makes modeling something as a monad as he has done completely without benefit. This would be quite accurately referred to as cargo cult programming.
A monad explanation that is not cargo cult mathematics would be one that explains what a monad is in category theory (a functor with two associated natural transformations), explains it as one of many other patterns, and explains why simply including monads and these other patterns in your code does not automatically give them the Mathematical Seal of Approval™, especially when using a language with no support for abstracting over them.
> most programmers write code as in the language they first really used, regardless of the language they're actually using
I doubt that this is true. I can't quite remember, but I'm pretty sure the first language I ever used was Color Basic. I've forgotten quite a bit of it, and it has pretty much no influence on my programming.The first real programming I did was with structured imperative languages (Game Maker was the first thing I used for anything real). The code I wrote bore almost no resemblance to Color Basic, and while I was using these languages, solutions to problems using the tools of structured imperative programming came to mind much more readily than solutions using the style of unstructured Basic.
Later, I learned Haskell and functional programming. For the first while, everything I wrote was like a puzzle. I had to think for a long time to do things the functional way. But after having used it enough, while I'm in Haskell, functional solutions come just as readily as imperative solutions come while I'm in those languages, perhaps even more.
Of course, people who are experienced with one language and inexperienced in another will try to apply the idioms from the first language in the second whether they're applicable or not, but programmers do not always write code as in the first language they really used.
> Myth #1 - Functional programming is Lambda Calculus
Of course functional programming is not just lambda calculus—there are actual data types besides functions—but I don't think this is what he's arguing. He's trying to argue that “simultaneity” (which I think he made up) isn't satisfied, and somehow conflicts with lazy evaluation. This is only a valid argument about lazy, impure languages, and I don't think any exist. All of the differences he's claiming exist between lambda calculus and functional programming don't exist between lambda calculus and Haskell. > In point of fact, the simultaneity constraints inherent in lambda calculus have been shown to make it unsuitable for issues involving concurrency (like multithreading), and for problems like that, it's better to use pi-calculus.
Whaaat?! Purely functional programming is much better suited to concurrency, because it completely satisfies “simultaneity constraints”, which makes race conditions, etc. impossible. > Myth #2 - Functional programming is 'different from' imperative programming.
...
> Church's thesis, upon which we base our whole definition of 'computing', explicitly states that Turing machines, lambda calculus, while programs, Post systems, mu-recursive grammars, blah-de-blah-de-blah, are all equivalent.The entire point of the Church-Turing thesis is there can be completely different models of computation that can compute the same problems, not that they're all the same concepts underneath.
> For those quick thinkers out there who've noticed that this puts Myth #2 in direct contradiction with Myth #1, KA-CHING! you've just won our grand prize!
I'm not even sure what he means here. The statements that functional programming is based on lambda calculus, that it is different in significant ways from imperative programming, and that all models of computation can solve the same problems are not at odds in any way. > Myth #3 - Functional programming is referentially transparent
This is where I get the impression that he has never actually used a functional programming language in his life, and he completely misunderstands what “referentialy transparent”. Haskell, barring its unsafe* functions, is indeed referentially transparent, which means that expressions will always have the same value no matter when they're evaluated. His use of Perl examples, using features of Perl that directly violate referential transparency, to demonstrate that functional languages aren't referentally transparent, is bizarre. > Myth #4 - Functional programming doesn't support variable assignment
I don't even know what he's trying to prove in this section. Yes, functional programming does allow you to iterate functions on values to produce new values. Haskell's standard library provides several utility functions for doing just this. The point of not supporting assignment is eliminating mutable state, which results in referential trasnparency, and not letting the effect of one part of your code leak out in hard-to-predict ways and affect other parts of your code, both of which it does successfully.His example makes no sense at all. He shadows a variable and then pretends that it's the same as the parent variable so that he can prove that variables don't always stay the same, because `x == y` is true in one scope and false in another. This doesn't change anything about referential transparency or having assignment, because referential transparency doesn't say that if you copy and paste an expression into a different scope it will have the same value.
I wish that instead of getting angry at other people for misunderstanding functional programming, he should try a little harder to actually understand it himself.
So what if it's "just" syntactic sugar anyway? Calling functions in C is just syntactic sugar on top of messing with the stack. You could make the claim that any feature of any programming language is just syntactic sugar on top of something lower-level, eventually getting down to the circuitry. But that's not a terribly useful criticism.
The definition of OO to me is a certain kind of polymorphism, which is fit by class hierarchies in Java, Ruby, etc., multimethods in CLOS, and typeclasses in Haskell (although Haskell in general isn't OO), although of course, that's not an authoritative definition either.
But good suggestion. :)
Both of them should be separate Google apps with Gmail notifications, but no Gmail integration. Wave could still have been invite-only for scaling reasons, but both of them would still have had better reception this way.
It wasn't a logic language; you actually created new syntax for the language, with BNF rules.