Why learning Haskell makes you a better programmer
dubhrosa.blogspot.co.uk
dubhrosa.blogspot.co.uk
To me, the most powerful example is this: "It forced me to think of every such problem as a chain of the primitive list operations, maps, folds, filters and scans. Now I always think in these terms. I "see" the transformation of a container as a simple sequence of these operations. "
That's a higher level of thinking that is faster and yet at the same time more precise than thinking in terms of imperative loops.
Haskell has taught him to think more powerfully.
Even more so than Haskell.
It doesn't have monads and the kind of abstractions that lets you modify control flow semantics. It doesn't even have the facilities to build abstract data types - which makes you work with less-abstract data, and realize that although some abstraction is useful, most of what is practiced today is useless.
APL / J / K promote, at the same time, container abstraction, and concrete down-to-the-metal real work.
Maybe more inspiration than introduction, but the "Game of Life in APL" video is a must see (http://uk.youtube.com/watch?v=a9xAKttWgP4&fmt=18)
The description on YouTube points to a "Game of Life" tutorial at http://tryapl.org. I haven't tried it, but it looks nice.
For a more academic (as in "read about, don't play with") introduction, I found Iverson's "Notation as a Tool of Thought" (http://awards.acm.org/images/awards/140/articles/9147499.pdf) a good introduction.
At least, I believe that is true. Someone stronger in CS theory could come along and verify/correct that statement.
This paper [1], for example, appears to argue that FOLD (foldr in particular) is universal, meaning that there is only one way to move through a list and resolve it to a single value. In other words, you may have some other algorithm that you think is not a FOLD, but if it moves through a list and resolves it to one value, then your algorithm is FOLD. (I say the paper "appears" to claim that, because in all honesty there's about 10-15% of the paper that I didn't fully grok).
I suspect that applies to map/filter/etc. After all, if you look at their definitions, they contain nothing but the essence of the operation (map/filter/fold/etc) itself.
So, map/filter/fold/etc are THE operations on lists.
You mean these functions - http://karma-engineering.com/lab/wiki/Tutorial5
Why Haskell, then?)
We can instead give tree a proper type, give operations over the type proper signatures which help to explain what the operations do, and get verification from the compiler at the same time that we aren't mixing things up.
It's hard to beat data Tree a = Node a [Tree a]
separation of effects from pure functions - this I cannot understand. Aren't these function pure?
And another part of "properness" - each node of a tree is a tree itself. Sometimes less types is more.)
Well, I'm old-fashioned, but I think that being able to put any kind of data into a list is the strength, not weakness of a language, and that user-defined ADTs and compound data-structures needs no explicit typing, they are nothing but conventions.
Types provide one important thing: static guarantees. Compile-time type checking can only work with strong, expressive data types, and ADTs provide a very good way of enforcing that. (Together with Haskell's `newtype` keyword, which really is nothing more than a convention, if you will, a different way of expressing a type synonym, but with the static guarantee that when you make up a `newtype Name = Name Text`, you cannot use `Name` wherever you can use `Text`, but you can access the `Text` `Name` is a wrapper for easily.
This makes it much easier for your intent to be expressed directly in the code, namely in the data types. Just compare:
makePerson :: Name -> Age -> Gender -> Address -> Person
makePerson' :: Text -> Int -> Text -> Text -> PersonThe author seems to be conflating IO and side effects. IO is just one of the many ways that side effects can occur.
> Purity also means you can't read or write global variables, that's just another type of IO.
Er... what? No, not at all.
Reading global variables is not side effects, much less IO.
Writing global variables is side effects, but has nothing to do with IO.
> Functions that do IO are difficult or impossible to test.
Absolutely not, IO functions are not harder to test than regular ones. Is your function creating a file? Start your test, assert the file doesn't exist, call your function, assert your file exists. Done.
> I now put all my IO in top-level functions that are called directly from main
This is great for homework, but in the real life, IO needs to be performed throughout your entire application, and unsafePerformIO is just not an option. You have to use iteratees for this kind of operation, which adds a pretty thick layer of complexity to your code base.
> boo :: Map Integer String -> String -> Integer
This syntax is clean but it doesn't tell me anything that
def boo(map: Map[Integer, String], key: String) : Integer
doesn't tell me.
> The point is, you can't confidently reason about a function from its signature if IO is involved.
Sure, you can: the mention of IO tells me this function operates within the IO monad, so I know a lot of operations and laws that apply to it.
Haskell is a fantastic language but this article is doing a very poor job at explaining why. I suspect its author has only recently started using Haskell and would have been better advised to wait a bit before sharing his enthusiasm.
> This is great for homework, but in the real life, IO needs to be performed throughout your entire application
My experience has been that I could get away with pushing I/O out to the fringes of the app and make 80-90% of the code pure. But everybody lives in a different "real life" and it's not unlikely that the kinds of things I do on the side for fun just don't require it.
I fully concur with the rest of your remarks.
> Absolutely not, IO functions are not harder to test than regular ones. Is your function creating a file? Start your test, assert the file doesn't exist, call your function, assert your file exists. Done.
They are harder to test (although definitely not impossible). What if your function does a network request, and the result is highly dependent on latency etc, sure you can mock an endpoint that responds with the various properties desired, but it's much harder than just using Quickcheck or HUnit or one of the other testing libraries.
> > Haskell makes your functions pure by default. You have to change the type signature if you want to do IO
> The author seems to be conflating IO and side effects. IO is just one of the many ways that side effects can occur.
Indeed, but that does not refute the OP's statement.
> > Functions that do IO are difficult or impossible to test.
> Is your function creating a file? Start your test, assert the file doesn't exist, call your function, assert your file exists. Done.
Right, and (in my opinion) that is more difficult than testing something that doesn't create a file.
> > boo :: Map Integer String -> String -> Integer
> This syntax is clean but it doesn't tell me anything that
> def boo(map: Map[Integer, String], key: String) : Integer
> doesn't tell me.
Indeed, but again that does not refute the OP's statement which was simply that the syntax is clean.
> > The point is, you can't confidently reason about a function from its signature if IO is involved.
> Sure, you can: the mention of IO tells me this function operates within the IO monad, so I know a lot of operations and laws that apply to it.
You do? Please tell me a law that applies to IO.
How about three? Right identity, left identity and associativity.
That's for the laws. For the properties, IO is a monad so I can compose any value in the IO monad with other monads (Maybe, ST, etc...).
Actually, I kind of wish IO was split up a bit... I have been meaning to play with rolling some typeclasses (with an IO instance and a testing instance) that wrap up IO of various types, so I define my functions in terms of these and spot what kind of IO various functions might do - I'm not yet sure how much of a win it will be, but it seems like something to explore.
http://www.haskellforall.com/2012/07/purify-code-using-free-...
http://www.haskellforall.com/2012/06/you-could-have-invented...
{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving, FlexibleInstances #-}
module CrWr where
import Control.Monad.Free
import Data.IORef
data RWRefF t a = NewRef t (IORef t -> a) | PeekRef (IORef t) (t -> a) | PutRef (IORef t) t a
deriving Functor
newtype RWRef t a = RWRef (Free (RWRefF t) a) deriving Monad
newRef :: t -> RWRef t (IORef t)
newRef t = RWRef $ liftF $ NewRef t id
peekRef :: IORef t -> RWRef t t
peekRef r = RWRef $ liftF $ PeekRef r id
putRef :: IORef t -> t -> RWRef t ()
putRef r t = RWRef $ liftF $ PutRef r t ()
interpret :: RWRef t a -> IO a
interpret (RWRef (Pure a)) = return a
interpret (RWRef (Free (NewRef t f))) = newIORef t >>= interpret . RWRef . f
interpret (RWRef (Free (PeekRef r f))) = readIORef r >>= interpret . RWRef . f
interpret (RWRef (Free (PutRef r t next))) = writeIORef r t >> interpret (RWRef next)
-- Highly specialized class of IO monads
class Monad m => RWIntRefMonad m where
new :: Int -> m (IORef Int)
peek :: IORef Int -> m Int
put :: IORef Int -> Int -> m ()
toIO :: m a -> IO a
instance RWIntRefMonad (RWRef Int) where
new = newRef
peek = peekRef
put = putRef
toIO = interpret
incr :: RWIntRefMonad m => IORef Int -> m ()
incr r = peek r >>= put r . (+1)
main = do r <- newIORef 0
-- We need to specialize before the m gets erased
toIO (incr r :: RWRef Int ())
readIORef r >>= printThere's a section on this practice in Real World Haskell: http://book.realworldhaskell.org/read/programming-with-monad...
I would point out that in addition to the mentioned testing use (itself not to be underestimated - QuickCheck is amazing and making more things tractable to QuickCheck is great), there's also some other big potential wins. If I have a function that deals with local files in a monad that's an instance of MonadHandle, I can write an instance for a RemoteIO type that performs file operations on a remote system through a network connection, and suddenly the function that was local also works remotely.
> This syntax is clean but it doesn't tell me anything that
> def boo(map: Map[Integer, String], key: String) : Integer
> doesn't tell me.
OP's point was not so much that the Haskell syntax is clean, but more that there is a huge difference between
Map Integer String -> String -> Integer
and
Map Integer String -> String -> IO Integer
The latter is allowed to do IO, while the former is not. There are very few languages that allow you to express this difference and have the compiler enforce it (Haskell being one of them). So "Map Integer String -> String -> Integer" tells you a lot more than "def boo(map: Map[Integer, String], key: String) : Integer" does.
For more information about what exactly types tell you, look up parametricity or read Philip Wadler's "Theorems for free".
Also Fortran being one of them (you can mark functions and subroutines 'pure').
When you call boo function in Haskell, you have the power to create the "universe" which is both necessary and sufficient for the function to perform some self-contained computation. When enough of these computations are composed and nested together, you can really see how data flows through some logical procedure, possibly one with lots of essential complexity. On the other hand, when I look at the same signature translated into Java, for all I know it could be the 4th function call in a stateful 8-function-long set of functions you always have to call in order, except you don't have to call the 6th function if you pass true to the 2nd function. Easy enough, right?
The fewer combinations of arbitrary state are allowed to determine the behavior of your program, the fewer unanticipated scenarios arise. I think this is a good software engineering principle. The corollary that I think Haskell really brings home when it comes to designing programs is that it's better to change the way data flows in one small section than to add a little more state somewhere and handwave away state combinations which you think are impossible. This idea applies in both Haskell and Java.
There are a lot of things you're not testing for. Permission issues, etc. Besides, you have -plenty of functions doing IO on databases, or doing parallel operations. That's definitely a pain to test.
> > boo :: Map Integer String -> String -> Integer
> This syntax is clean but it doesn't tell me anything that
> def boo(map: Map[Integer, String], key: String) : Integer
> doesn't tell me
It tells you it's not doing IO. Besides, what about this:
def boo (foo: Some Obj): void
What does this do? Write to a file? Change a field? I don't know.Testing IO involves systems and context that are external to both your code and your tests. This makes them more complex to set up and more error prone.
Large Haskell applications benefit from different overall design than the typical C++ or Java app. Our very large Haskell codebase doesn't use IO much nowadays, and as we go back through to refactor older code it diminishes even more.
Basically, I wish I could understand Haskell so I could harness its power.
[1]: http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_H...
i had a list of lesser known haskell tutorials/cheats/ search engines here
https://news.ycombinator.com/item?id=4927532
but i forgot the Stanford course notes
http://www.scs.stanford.edu/11au-cs240h/notes/basics-slides....
____________
for all those squiggles like (>>=) use hoogle, index of RWH hardcopy or:
http://rigaux.org/language-study/syntax-across-languages-per...
http://symbolhound.com/?q=haskell+%3C%24%3E
and bookmark these 3 pages
http://www.haskell.org/haskellwiki/Keywords
http://hackage.haskell.org/packages/archive/base/latest/doc/...
http://hackage.haskell.org/packages/archive/base/latest/doc/...
______________
also, I think more time reading code, esp type signatures, and less time reading language spec and descriptions of syntax vs. O-O pantheon:
I felt much the same way as you when I first decided to dig into Haskell and went through a period of some frustration in which I felt that, although I was more or less understanding the literal text of the books and documentation I was reading, I was lacking a feel for the "big picture" of how it all fit together. I could explain specific details of how the language worked but I couldn't really visualize how to put together a complete, useful program in it.
For whatever reason, something about Haskell really intrigued me despite my frustration, and I kept at it. There was never any major epiphany where everything just clicked, though there were a few minor moments of enlightenment, as when I discovered that the neat pattern I had started using in some of my code actually had a name and a well known type class: "Applicative". I also recall reading a particular article about monads (and manually working through its examples) that made me suddenly realize how utterly simple they were, despite the ridiculous hype.
After writing enough Haskell code, it eventually dawned on me that I "got it". I wasn't struggling anymore. Haskell had, without my noticing it, become my default, natural way of thinking and reasoning about code, and it just made sense in a deeply fundamental way. I could no longer even see why I'd been confused in the first place.
Professionally, I've used mostly Java and Ruby, and have never written a single line of Haskell for pay. But like the author of this article, I've found that it shapes the way I write code in other languages, even those far removed from pure FP, in significant ways. My mental design process when writing new code at work usually takes place in terms of Haskell's data types and abstractions, and then gets translated into whatever language I'm actually using once I feel like I have a good handle on the direction that I'm heading. This usually results in nice, clean designs, consisting mostly of pure, composable functions and immutable data with as little shared state as I can get away with, arranged, when necessary, around an imperative, IO-driven core. Basically the same way I'd structure actual Haskell code.
If you're intrigued enough to keep at it, then patience is all you need, and you'll get there. It's pretty fun when you do.
If you've gone beyond a few layout steps, consider factoring out more small functions from your code. I've found that even very large Haskell codebases tend to end up with mostly two or three types of functions: short single-purpose functions of a few highly expressive lines, long but conceptually simple pattern matches covering a data type with many constructors, and long but conceptually simple monadic functions sequencing a flat series of steps gluing together other functions.
You'll spend most of your time reading and writing the short functions, writing types and names that make them self-explanatory, finding ways to factor out more of them, and occasionally staring at them and thinking "it feels like I've missed some simpler way to write that"; the long functions will fly off your keyboard almost as fast as you can think and type them.
At some point I decided that there are things I like and am passionate about that I'm simply not going to be able to master for one reason or another and I'll have to live with admiration from afar. The last time I used Lisp I came away feeling like I had successfully tricked it into doing what I wanted, and I looked back at what I had written and thought, this is no way for me to live. I suspect what you're describing is the same thing.
This is precisely why learning Haskell is important...at least for those interested in self improvement. When I started Haskell, I picked it because it seemed like the hardest language I knew of at the time. It was one of the best things I've done for my career.
boo :: Map Integer String -> String -> Integer
And here it is in C++ as a standalone function: template <class Key, class Value> Key boo (const Map<Key, Value> &, const Value &);
or, more idiomatically, as part of a class: template <class Key, class Value> class Map {
[…]
Key boo(Value &val);
};
The author's point is that the Haskell code can't do much other than use its parameters, because there is no IO involved, whereas the C++ version could do anything. Well, sure, it could, but let's be reasonable (which is all we can be without reading the code): it's probably gonna map a string to an int. It's just as likely that the Haskell code could do weird stuff (how does it behave if it finds multiple candidates for Value?) as the C++ code, practically speaking.So this argument actually turns out to be an argument for good containers, or, more generally, for good built-in types. The author could make all those assumptions about his function from the type signature because he/she knew what "Map" meant, not because of anything inherent in Haskell's type system. I mean, I grant that Haskell has a great type system, and great built-in types. It's just that this example doesn't make the argument it claims to make.
Let's look at some code I'm writing now. Here is strstr:
boo :: String -> String -> Int
Not too helpful really. Here is a function which searches ComplexDataStructure, which contains file and directory names as well as other data, for partial matches of "string", returning a "goodness" value indicating the closeness of the match for the highest-matching candidate: boo :: ComplexDataStructure -> String -> Int
I challenge anyone to work out what the function does from that. Sure, you can make more assumptions about the Haskell version, but, practically speaking, IO cleanliness has little to do with it. What matters here is knowing what the data structures do, and using decent types. boo :: Map a b -> b -> a
since it nowhere mentions int.Here, you can't just magic into existence or modify an unknown 'a' value.
boo :: Map a b -> b -> Set a
I'm not sure how you would conjure up just an 'a' in general. boo :: Map a b -> b -> Maybe a
since Haskell doesn't have null inhabiting every non-primitive type.Also, while we may all choose to "trust" the C++ standard library, where it really comes in handy is reasoning about code written by your team on a day to day basis and being able to quickly re-factor code you did not even write.
foo :: forall a b. a -> b -> a
is inhabited by precisely one program foo a b = a
Furthermore, you have the opposite effect such as the nonexistence of functions with types like freeCoerce :: forall a b. a -> b
The best you can do is write safeCoerce :: forall a b. a -> Maybe b
which sometimes works but is allowed to fail if the coercion isn't possible.---
Taking this to a logical extreme you can extend a Hindley-Milner type system to include dependent types and then construct meaningful types (theorems) which are only inhabited by correct and obvious programs. For instance, this Agda type
sort : List a -> Sorted a
maps lists not just to other lists but instead the type of sorted lists guaranteeing the meaning of the function 'sort'.---
Obviously any static typing system inherits a bit of this power. There's no doubt that reading the signature of a C++ function will help you to make educated guesses at what's going on. Furthermore, a malicious Haskell library implementor could use the "unsafe" family of functions to create something like 'coerce' or a function that has impure side-effects but doesn't end up in the IO monad. There is some amount of trust that libraries do not abuse such unsafe commands and the IO monad can be guaranteed to isolate impure effects---but so long as that isn't violated, the types can be incredibly informative.
---
It's also worth noting that Haskell does have "bottom" inhabiting every type, similar to null but denoting infinite loops instead of null values. So you actually can write coerce without using 'unsafe' commands like
coerce :: a -> b
coerce _ = let bot = bot in botThat said, the choice of a C++ map as your example is unfortunate but illustrative. :)
The bog-standard mapping type in C++ is std::map, which has a method like this:
T& operator[]( const Key& key );
One might think, based on the type and how this works in other programing languages and even from reading other people's code, writing `mymap[foo]` would just be an access. But it's not! If the `key` is not in the map, it inserts an item T, presumably calling the default constructor.It's a good example of how, in general, the semantics of a lookup for a non-existent key varies by language. You have to use std::map::find() in C++, which returns an iterator which will equal std::map::end(). Python generates a KeyError exception (!). In Ruby and Clojure, you get nil, but note that you can have nil values in a map. :) Java returns null.
The Haskell signature for lookup is this:
k -> Map k a -> Maybe a
What's going to happen is encoded in the type system. In general Haskell is good about this; Maybe indicates possibility of failure, lists suggest nondeterminism, etc.That said, as others have pointed out, Haskell functions often end up being really generic. The language sort of pushes you in the direction of not specializing on types but rather putting looser restrictions, like typeclasses, on them. Type synonyms can help, too, as mentioned above.
Here's what you can do in Haskell:
boo :: (Eq b) => Map a b -> b -> a
This would describe that all your function could possibly do inside is just using eq operation with values of map items.Aside from that it is an amazing language that really kicks you to think differently.
"I've recently finished reading Learn You A Haskell For Great Good!, and I think the author's approach to monads works well. I'd read various metaphorical explanations that only served to further cloud the issue, but thanks to LYAH I finally got it.
In short, he has you using monads long before you understand them (Maybe and IO in particular), then slowly introduces monads by first explaining functors and applicative functors.
In retrospect, the mystique seems crazy. Monads are just not that confusing: they're simply values with added context, along with functions that let you interact with those values without losing the context. It's a shame that this powerful idea is so obscured by its supposed difficulty."
---
I'm not going to give you any more metaphors (a monad is not a burrito). Monads are objects (in the category theory sense) that in some sense "contain" another object, and let you work on both the level of the contained object and the box around it. This can be terribly useful (and necessary in a pure language like Haskell).
It turns out that monads obey the so-called functor laws (which is a just another scary category theory word for a really simple idea) and thus have the function `map` defined on them. What's really cool is that we can map (transform) the boxed data inside the monad without caring whether there's actually anything inside.
Why is this useful? Consider the Maybe monad in Haskell (or Option in Scala). Lets say you want to get data out of a HashMap. You look up a specific key, but there might not be any data there. In most languages, this would be handled by returning a null or throwing an exception. Both solutions break type safety, so we want something better. Using Maybe we don't have to care whether or not the data is present to operate on it:
// This is Scala because my Haskell is a bit rusty, but the
// ideas are the same
val map = new HashMap[String, String]("hello" -> "goodbye")
// HashMap$get returns, in this case, an Option[String]
val opt1 = map.get("hello") // => Some("hello")
val opt2 = map.get("bonjour") // => None
// What happens when we process these two options?
opt1.map((x) => x.toUpperCase) // => Some("HELLO")
opt2.map((x) => x.toUpperCase) // > None
// Note that `map` here is the ">>=" operator in Haskell
// It's different than a normal map, which takes in something
// of type A, a function from (A -> B) and produces something
// of type B. ">>=" instead takes in something of type M[A],
// a function from (A -> B) and produces something of type
// M[B]. The powerful thing about this construction is that
// it lets us concatenate processing steps:
opt1.map(_.toUpperCase).map(_ + "!!!").map("!!!" + _)
//=> Some("!!!HELLO!!!")
opt2.map(_.toUpperCase).map(_ + "!!!").map("!!!" + _)
//=> Nothing
// If we start out with Nothing, we always have Nothing, but
// we don't need to check for it at every step.
No conditional code checking for nulls, just type-safe processing on possible unreliable inputs.And Maybe/Option is possibly the simplest monad. You're probably more familiar calling map on lists, and it turns out list is also a monad, with the empty list representing the Nothing value. Things get trickier when we move into the work-horse monads in Haskell: IO and State, but the same principles apply. The IO monad lets us process the results of running IO operations without having to care (until we need to) about whether the options have succeeded or not. It also, crucially, allows us to run IO operations in a determined order by chaining the operations together. The details of all this are too long for a HN comment, so I'll just direct you to Learn You a Haskell [0] if you haven't read it, or Real World Haskell [1] if you have. Both have pretty good chapters on monadic thinking which hopefully will get you over the hump.
My best advice is: don't be scared by the category theory. Contrary to popular opinion, you don't need a PhD to program in Haskell :).
Good luck!
[0] http://learnyouahaskell.com/chapters [1] http://book.realworldhaskell.org/read/
// of type B. ">>=" instead takes in something of type M[A],
// a function from (A -> B) and produces something of type
// M[B].
The type of ">>=" is M a -> (a -> M b) -> M b, which lets you 'chain' the result of one monadic function (a -> M b) to another monadic function. So, for example: return :: String -> IO String
putStrLn :: String -> IO ()
getLine :: IO String
return "foo" >>= putStrLn :: IO () -- prints "foo"
getLine >>= putStrLn :: IO () -- gets a line of input, then prints itUsing global state is like blowing Gabriel's horn -- everything dies!
Ok, so not everything, but the upshot is that global state plays very badly in the future of computing.
Learn You a Haskell For Great Good! is one of the better introductory programming books I've read, and the author writes in a ridiculously fun and easy-to-read style.
But if you find 'fun' and large amounts of text distracting, then Graham Hutton's 'Programming in Haskell'[1] is a better introduction to what Haskell is about.
If you've written so much Haskell that you think of global variables (!) as a form of IO, then perhaps you would benefit from writing in C for a while? Beware internalizing abstractions so completely that you confuse them with the reality.
Writing a global variable is a side effect, because you change something other than the return value of a function. Reading would still break functional extensionality: Calling a function with equal arguments might yield different results in different contexts.
> I have no idea what the word "boo" is supposed to mean when used as a function name. But I can be almost certain what this "boo" does. It takes a map that has Integer keys and String values, and a second argument that is just a String, and it returns an Integer. So I'm fairly sure that this function does a reverse mapping
* Counts the values that have the second argument as a substring
* Returns key that, using some fuzzy string algorithm, matches the second argument the closest
* Computes a score from matching the second argument against the map, which tracks document occurrences in a search engine type gizmo
I don't think the meaning is obvious at all. If anything it's a lesson in naming stuff: Don't call your functions "boo".
invMap :: forall k v. (Eq v, Eq k) =>
Map k v -> v -> Set k
The parametricity means that while this function may get specialized to have v ~ String and k ~ Int, we can be sure that invMap isn't using that information. Instead, with the typeclass restriction, we know that the only thing the function can be doing internally to the ks and vs is comparing them for equality.Intuitively, invMap can either be computing the Set of ks which hold a particular v or, maybe, the set of ks which don't hold that value and not much else.
A lot of Haskell's power comes from it being able to enforce the validity of this style of programming. C++ does not - and when working with C++, you have to reason at both the C++ level like you always do, and at the Haskell level (but without the compiler verifying your work).
That's extra burden without extra benefits.
No. While concept is nice, in practice, with C++ it can result in time waste and unreadable/unmodifiable code with no clear benefits. Cleaning up some statefull functors overdesigned by some intellegent fool can be a gruesome job.
This makes no sense at all. A functor is a type class, it doesn't contain any state.
In C++, it makes sense: http://www.sgi.com/tech/stl/functors.html. Functors are function objects, which is to say they're a class/struct with one method, operator().
If this makes you sigh, know that you're not alone.
If you are going to be judgmental and calling people fools, I suggest improving your spelling first, or you'll just appear to be one yourself.
I wanted to describe a coder with little experience; tendency to overdesign; tendency to use 'design patterns' with no reason; tendency to build very elaborate designs; tendency to use the same pattern everywhere.
I think that "intelligent fool" and maybe "baby with a hammer" fit that description.
As to spelling errors, I'd suggest to try ignoring these and try to get the gist instead. Quite a few people here (including me) are not native English speakers.
There is one question, before you press "down-vote" few times - what is the sequence of machine instructions your monad compiles into?
The thing is, most popular languages these days support higher-order functions. It's nothing to write home about. Monads get a lot of attention specifically because they are a fairly unique idea that is not supported out-of-the-box in other languages. People who are advocating (or evangelizing, if that's your perspective) for a particular thing are going to focus primarily on the specific aspects of that thing that make it stand out from the crowd. For Haskell, that means things like monads, algebraic data types, non-strict evaluation, functional purity, etc.
There is one question, before you press "down-vote" few times - what is the sequence of machine instructions your monad compiles into?
I can't understand what you're getting at here. What difference does it make what code the compiler emits, unless I am trying to aggressively optimize low-level performance? If it does what I intend it to do, and is reasonably fast at doing it, I don't care about the specific instructions. How is that relevant?
The claim that the notation is unreadable is similar.
Haskell's notation is certainly unfamiliar, and that can be frustrating (as all unfamiliar notations are), but it is one of the best designed ones out there.
As for the question: Why learn the type system? Because it helps you encode your program's invariants as types -- allowing them to be verified cheaply and repeatedly by an automated prover (the type checker), rather than being documented and checked by expensive tests.
They are also documentation you can rely on, as it is guaranteed to stay in sync with the program. It means you can get up to speed with other's code much quicker.
They also restrict code in useful ways, encouraging code that is more concurrency-friendly, simpler, and generally better.
I think, looking at sources of arc.arc or news.arc, that it is, how to say, over-hyped.
Java's "strong typing" doesn't protect you from null dereference errors, as one example out of many.
Also, strong typing is not as good when you have to specify a lot of the types repeatedly. Without good type inference, it loses a lot of the appeal.
Strong typing is not nearly as good when every function has arbitrary effects not specified by its type.
Strong typing is not as good when it is harder to do parametericity, as discouraging it means you get monomorphic types, which are far less effective at preventing errors.
Haskell gives a whole lot more benefits via static types than Java does.
The article was about pure functions as opposed to stateful functions, clean syntax that gets out of your way, and higher-order compositions over container types. It was about good habits that can be taken to other languages.
> what is the sequence of machine instructions your monad compiles into?
What is the relevance? If you're using Haskell, then machine code should be the last thing on your mind. If you're writing code to produce the most efficient machine code, then you should be writing C. The entire point of higher level languages is that they abstract away the gritty, lower level concerns like 'what register should I place this pointer in?'.
I spoke against a concrete and specific thing, this very article, what more "examples" do you want? Pointing to a particular thing is the very definition of an example.
So, yes, this kind of article is very often seen, and we're very familiar with the genre to need more examples. It even admits so itself, as it starts with: "It's OFTEN CLAIMED that learning Haskell will make you a better programmer in other languages.".
As for the articles' examples: meh. Using Haskell signatures to sketch public APIs in other languages is somehow better that using any other similar notation? Even if it was so, he never proves it better. Just some handwaving. And his other big point is about thinking in terms of maps, sets, filters, reduces and such. How is that unique to Haskell? Lisps beg to differ...