> This is one reason why you should learn Haskell: you learn to how to build flat architectures.
I suspect that most people learn the advantages of flat architectures with experience using any programming language. For me, I started to "get it" with Javascript & Ruby. So Haskell hardly has a monopoly on this.
I understand that Haskell has a different take on this. It seems the author has found a pattern in Haskell that he frequently uses.
Ideally, I can use patterns in multiple languages, so my experience can seamlessly transfer.
This often means there's a lowest common denominator to a particular pattern. I'm afraid that if I learn a pattern unique to Haskell, that it is not applicable to any other language. I suspect that there are underlying & common principles that are expressed differently with Haskell.
These are not patterns unique to Haskell. In fact, it's just the opposite: these are mathematical abstractions, and so they can be used anywhere that you have use of function application, in stark contrast to the alternatives. By using these patterns instead of Ruby or Javascript-specific, Application-specific, or Object Oriented-specific patterns, you can be more confident that your abstractions will be consistent with eachother, and as a result, more composable.
Case in point: I'm currently developing in Javascript, but I use these patterns (Monad, Applicative Functor, etc) to manage things like asynchronous code and error handling, because they're far more flexible/composable and reliable (in terms of not having side effects) than the other offerings on hand, and I know that will always be the case, because these abstractions were designed with the constraint of composability in mind: in fact, they are, by definition, the minimum set of constraints required to get these emergent properties. The benefit that Haskell itself offers is that it can check at compile time that these constraints hold true.
> I'm currently developing in Javascript, but I use these patterns (Monad, Applicative Functor, etc) to manage things like asynchronous code and error handling
Thank you for this comment. I'm interested in knowing which these fundamental patterns that Haskell emphasizes.
Unfortunately, time is limited & currently, I'm not able to allocate enough to learn & use Haskell in a meaningful project. I recognize that there are some important abstractions & I'd love to learn all of the useful abstractions that are applicable to other languages & environments.
I think his argument is that these patterns are the easy thing to do in Haskell, not that they are necessarily unique.
The Monoid pattern is universal, but most languages don't give you tools to talk about it as an entity.
It is harder to enforce something like implementing a Monoid Ruby or JavaScript but you can always implement mappend, mempty and use "duck typing". Haskell's advantage in encoding these patterns is static checking, and type classes.
> Ideally, I can use patterns in multiple languages, so my experience can seamlessly transfer.
In many cases you are not guaranteed via the type system that one individual's implementation of the Observer pattern (for example) is not full of bugs, contains side effects, or closely adheres to the pattern's spec.
I believe Gabriel is hinting at the theoretical underpinnings of what it means to be composable, as opposed to just talking about how things can be mashed together (via pattern or interfaces or contracts or testing or whatever).
> In many cases you are not guaranteed via the type system that one individual's implementation of the Observer pattern (for example) is not full of bugs, contains side effects, or closely adheres to the pattern's spec.
Nor are you in Haskell, of course. Nothing about that language prevents code being full of bugs, nor failing to adhere to a specification, nor, given the existence of unsafePerformIO, having side effects.
Certainly, a competent Haskell programmer will not write bugs, will adhere to the specification, and will not introduce hidden side-effects. But then, nor will a competent programmer in any other language.
What is the barrier to "stupidity" in a language. In Haskell there is a deliberate, powerful barrier in the form of a compiler which will yell at you repeatedly until you stop violating the constraints encoded in the types. You either learn to work along the lines laid out by the types or you cheat.
To cheat you import a module called System.IO.Unsafe or Unsafe.Coerce and get sudden access to a magic hammer. If you use it to subvert the type system in a dramatic way then you'll immediately get so many massive runtime errors that you'll be forced to rescind. If you use it on a project that enforces -XSafe then the compiler will still reject you.
To get past both of those barriers you probably have to have a really, really great desire to break the system or a deep understanding of why and when it's valuable to use these unsafe types.
Again, nothing is actually stopping a terrifically bad programmer from breaking your statics, but there are a lot of things making their life much harder in the process.
The wager is that you can detect and avoid people who are willing to cross higher barriers much more easily.
A particular side-effect that an incompetent Haskeller can (and will, multiple times) introduce is wiriting code that gives correct results and is seemingly correct, but will suddenly run out of memory on larger (but still reasonable-sized) data due to unexpected laziness.
In practice this doesn't happen very often. Laziness is unique to Haskell, but the phenomenon that bad programmers can introduce subtle bugs is most definitely not.
You can encode a specification within Haskell's type system and get guarantees that are very difficult in other languages. Gabriel's pipes library is a great example of this (see a Pipe's associativity laws).
Oh, sure. Haskell's type system is famously powerful. But the simple fact is that it does not preclude bugs. It is possible to have bugs in Haskell programs!
Nobody who knows what they're talking about says that Haskells types (or even Idris/Agda/Coq's types) "preclude bugs" but instead that they "eliminate some classes of bugs so long as you don't behave really pathologically".
That's a much weaker statement, but still a valuable property.
I did not say it was impossible to write Haskell with bugs. I simply stated that you are not guaranteed that a particular implementation of a design pattern adheres to the patterns semantics, therefore introducing a bug. With good usage of Haskell's type system, you can wholly avoid such bugs, and a great example of this, as I stated, is the associativity laws within the pipes library.
No language "precludes bugs". You can get bugs because you didn't leverage Haskell's type system ("let's use String for everything"), logic bugs, or simply bugs caused by a faulty understanding of the problem. What you're not going to get is NPEs, or unwanted side-effects. Since it takes very little work in Haskell to ensure statically that two numbers which represent two different notions (say, mass and velocity) have different types, it's going to be lot easier to get a robust program.
But no, Haskell is not a silver bullet, and comes with a number of significant drawbacks.
Obviously it's a difference of degree. But Haskell offers more assurances in some areas than other languages. There are escape hatches, but that's exactly what they are - things that any reasonable programmer shouldn't reach for unless they really have to. You can give a function the value 'undefined' and the compiler won't care. But that is really only meant as "not implemented yet", "there are no sensible value to give back for this value", etc, not par for the course like for example the way null pointers are used in some other languages.
But since the world isn't perfect and programs can always have bugs, I guess we should just throw our hands up and proclaim that it doesn't really matter what we use.
I suspect that most people learn the advantages of flat architectures with experience using any programming language. For me, I started to "get it" with Javascript & Ruby. So Haskell hardly has a monopoly on this.
I understand that Haskell has a different take on this. It seems the author has found a pattern in Haskell that he frequently uses.
Ideally, I can use patterns in multiple languages, so my experience can seamlessly transfer.
This often means there's a lowest common denominator to a particular pattern. I'm afraid that if I learn a pattern unique to Haskell, that it is not applicable to any other language. I suspect that there are underlying & common principles that are expressed differently with Haskell.