How to read Haskell like Python
blog.ezyang.com
blog.ezyang.com
I understand that this article is geared towards beginners, so this is probably just a simplification on the author's part. Other than that it's a great article.
It's true that most of these "effects" are implemented in terms of pure functional code, e.g. the state monad—which models a very imperative construct—is also pure and has no side effects from the implementation's point of view[1].
The point is that from one point of view, these are 100% pure: "Of course, it's implemented in pure terms, so of course it's pure!" There's another point of view that they're entirely impure: "I'm manipulating state in this function, so of course it's impure!" All of which is to say, if you ignore the plumbing, the water appears out of nowhere. [invocation of Clarke's 3rd law excised for triteness]
The same could be said about Haskell itself: "Of course it's impure, because there's a call stack being destructively modified as it runs!" (Conal Elliott went the other way and suggested that C was a pure functional language[2].) It's just that, of all the monads, some of them (e.g. the IO monad, the X monad, &c) use some kind of "magic" to interface with external effectful functions, while other ones mimic state using pure functional constructs. Still—to a programmer, the Cont monad appears to jump throughout your code, making it "effectful" from an appropriate level of abstraction.
[1]: http://en.wikibooks.org/wiki/Haskell/Understanding_monads/St...
[2]: http://conal.net/blog/posts/the-c-language-is-purely-functio...
I guarantee that: http://blog.ezyang.com/2011/04/the-haskell-heap/
x = foo()
y = bar(x)
baz(y)
Knowing that any of these functions could throw an exception. How is it any different, from the reader's perspective? (Yes, it's /very/ different from an implementation perspective, but that's not what we're dealing with here...)This article seems very helpful for simply enabling someone with Python experience to learn Haskell.
1. Look at the types of higher order functions (remember, these operators arent magical syntax, just functions) in ghci with ":t (>>=)" for instance.
2. Use hoogle to find the docs for a function, view its implementation, or search for "that one function" by type
P.s. never heard that fish operator business.
Some of my co-workers were interested in learning to read Haskell, not so much in writing it. Let's see whether they like it.
Enter any operator or other name, no matter how crazy the characters involved, and it'll find it if it's in the standard library. This contrasts with resorting to Google to look things up in most languages and despairing because it thinks your burst of punctuation (which is the whole point of the query) is noise.
Besides the standard arithmetic ones, only ones I can think of are the "fish" operators, composition dot, and a few arrows.
Did you try to learn what they mean, or did you give up earlier than that?
There aren't really that many, though some modules will abuse them. The standard ones make sense.
There are also the applicatives <$> <*> <$ etc.
Point-free style, best style. http://news.ycombinator.com/item?id=3233870
It's like math: if you just saw something like a triple integral over a weird region with little fiddly symbols everywhere, it wouldn't make sense. If you learned about them starting with the basics, the logic would be clear and elegant.
The other thing is that all the "do" stuff and 'fish operators" are actually really clever and really high-level abstractions--they are basically a unified language for describing all sorts of computations, not just ones with side effects. For just reading most code, this isn't important; for understanding the elegance of Haskell, it is.
This is essentially what I took from the article.
However, I still agree with the OP.
If you are already familiar with the language syntax, grammar, idioms, and so forth then it seems easy and trivial. I find a lot of Haskellers confused as to why anyone finds the language difficult and opaque. Many tutorials and articles are written to convince people that Haskell is easy to learn, I think, because they are familiar with it and desire others to be familiar with it too. Once one is familiar with the language it doesn't matter how dense and difficult it is.
The truth is that Haskell is difficult to learn. One does have to start learning from the fundamentals up because the fundamentals of Haskell are so different from everything else that it's difficult to relate to the previous experiences of most people.
Contrast this with someone who learned C or some dynamic scripting language like Python from the ground-up.. there are a lot of languages with enough shared grammar and vocabulary out there that they can reach out to them without abandoning their previous notions entirely about how programming is done.
Haskell may not exactly ask for one to abandon their notions of computing conceptually but I think it is fair to say that it does ask you to give up all of your previous knowledge of syntax and grammar in order to learn it.
Haskell is as hidebound to the ML school as Perl is to the curly-brace school, and they both suffer for it.
I'm by no means a Haskell expert, but one of the worst parts of learning Haskell (or reading arbitrary code) is that there's no special syntax for partial application. Certainly that's a benefit in many cases if you're familiar with the signatures of the functions you're using, but it's definitely not explicit.
Lack of special syntax for partial application can be confusing for a lot of beginners; it certainly is very confusing if you're implementing, say, the continuation monad. But I think a lot of people overestimate the extent to which partial application appears in normal code: usually you fill up all the arguments except the last one, which is shuttled in via =<< or something similar. You don't have to think too hard about it, because the typechecker will make sure you've put all the functions together properly.
Which is sort of worse sounding, but it becomes quite natural once you start writing using types. I think of it like using legos.
makeLegoman :: Legs -> Torso -> Head -> Legoman
makeLegoman some_legs :: Torso -> Head -> Legoman
makeLegoman some_legs some_torso some_head :: LegomanThere are also many things that update $_.
You can call the function
foo :: a -> b -> c
like this bar = foo a
or like this: bar = foo a b.
In the first case bar will be a partially applied function of type bar :: b -> c
and in the second case it will simply be a value of type c.Actually, as far as I know, this is actually what happens in the background. I mean that calling
bar = foo a b
actually gets converted to bar = (foo a) b.I am aware of this. This is the source of some of my frustration reading Haskell code. I know that you can think of functions as single-arity first-class entities, but I've never been a mathematician and I've never thought that way.
I don't have enough experience maintaining large Haskell programs over a long period of time to know, but I suspect that this layering approach might encourage the kind of convolution you occasionally find in CL or Smalltalk programs where it's easy to build a teetering tower of the wrong abstractions. Perhaps there's something different about Haskell that encourages a more careful code curation, but I don't know.
Alternately, perhaps it's just a flaw in the way I think about Haskell that I want a visual distinction between partial application and calling a function.
map{
tr/a-z/A-Z/;
print $_
}@array;
I don't remember the exact syntax, but the tr acts on $_ by default, and $_ starts out as each element of the array. my @array = qw/one two three/;
$_ = "Hello";
say;
map { tr/a-z/A-Z/ } @array;
say;
prints.... Hello
Hello
However remember it's a reference so using tr// will update @array... say "@array"; # => ONE TWO THREE`<-` really is syntax: http://www.haskell.org/hoogle/?hoogle=%3C-.
More literal and decoupled languages like Python or Lisp (or C) mostly work from the inside out in a predictable way where the results of a function don't depend on how or where you use it. Sometimes you couple to a global variable or something but usually that's bad practice and you can be careful to avoid it when it isn't necessary.
Haskell and Perl depend on type systems and context to determine what a function is supposed to do. The expression that receives the result of a function has to be interrogated to determine what the function might do and that can continue recursively. Perl uses $ and @ and Haskell gives you a lot more possibilities than that, but most Haskell depends on the local versions of @ and $ because those are the best contexts, as lwall knew. The context focus can be a nightmare or it can be a perfect concise crystal of exactly what you want but it isn't exactly the acme of separation of concerns. Haskell gives you a lot of rope to hang yourself here.
And then there's the way Haskell and Perl both love their proliferation of syntax, mostly sugar, and special symbols that make them look like line noise until you're an insider at the language club. And even then, they can still be line noise but you aren't supposed to admit it.
(Note: I've written Perl and Haskell programs but not used them for significant projects. Mostly I'm stuck with C++ and Javascript for the same reasons you probably are.)
You'll have similar problems in dynamic dispatch systems: what does this object do? It could do so many things. In Haskell, we demand that the object always follow a certain set of rules. This is very useful.
Haskell's syntax is very clean and readable, the examples in this article are probably intentionally a bit confusing or at least artificial.
And besides, when looking at a programming language, syntax is the _last_ thing you should look at. The semantics is what is important, by which I mean function calling conventions (fixed vs. curry vs. varargs), type systems (dynamic vs. static vs. inferred), type of evaluation (strict vs. lazy vs. unification) and so on.
Choosing a programming language based on it's syntax is like choosing a wife based on her looks only.
The fact that Haskell allows libraries to define their own operators is a crime against humanity that justifies the sort of criticism usually leveled at Lisp macros, which is that it's impossible to guess what they mean without looking them up.
When you're writing very abstract code that operates on abstract structures -- there's often no name that conveys the meaning. English and maths don't have names for every useful abstraction and abstract concept.
Instead of inventing some arbitrary name in English, you invent some arbitrary name as an operator, and that gives you infix syntax (often desirable for binary operators) and a recognizable visual signature.
Also, operators can greatly enhance readability, if you know a certain convention. Consider for example a vector library:
There are various arithmetic operators -- and those can be applied on scalars and vectors. Ordinary arithmetic operators operate on scalars. If < is prepended on the left side of the operator, then the left argument is a vector, and similarly with > on the right side. This makes it easy to immediately know what <+> means, vs what *> means, and so forth.
And of course, if you're like me, then you don't want to go into a language where there's no Twisted equivalent.
Twisted, Event Machine, and Node (yes, even Node!) provide this. Haskell should have a library which does, too.
Is Twisted like Node.js? If so, look here: http://stackoverflow.com/questions/3847108/what-is-the-haske...