I've been looking at learning Haskell for al the obvious reasons, but I've been put off by all the lengthy and detailed introductions to the syntax, and math oriented examples, that seem written by theoreticists.
What I would like (and in my opinion what Haskell needs to grow), is a more practical introduction, like doing a database conversion, or writing some network service. Syntax can be explained in passing. Does this exist?
This was me as well. I had tried several resources and books (including LYAH) and while I had enjoyed them, they hadn't seemed to "stick." I can't recall now where I saw Conrad Barski's tutorial [0] mentioned, perhaps here on HN, but I found his approach much more personally suitable.
He steps the reader through writing a program that reads in and then divides an arbitrary park map into small, roughly equivalent picnic plots with colored SVG output along the way. The code takes center stage and is presented to the reader with the why and a good/bad analysis afterward.
I'm not sure whether this is what you're after, but at the very least it's worth a glance.
I was pretty impressed with the content in "Real World Haskell"[0], it's available online for free and for sale in print. I don't remember all of the practical examples, but off the top of my head, they include wiring up a pcre library by using FFI into C and reading a barcode from an image. This talk claims that the book is out of date; that's probably true, but I still found it to be valuable.
Its not databases or network services but Write Yourself a Scheme in 48Hrs [1] is a more practical intro.
Aside from that I would recommend the cis194 Lectures [2] if you do feel ready to take a slightly more thorough top down approach. There are exercises for every almost every lecture and you can even peak at your digital neighbours to compare solutions [3].
This is kind of the problem, though: Haskell is popular among people for whom "write Scheme in this language" is an example of a practical introduction. It frequently seems like a community of PLT experts talking with PLT apprentices, with the workaday concerns of 95% of practicing developers treated as an exercise to figure out on your own time.
This isn't a criticism of Haskell or Haskellers. It's more an observation that the current makeup of the Haskell community might prove to be a drag on its appeal.
I teach a lot of Haskell [1] and observe a lot of people teaching Haskell. Write Yourself a Scheme is not representative of how the Haskell community teaches Haskell.
I can't even remember the last time it was cold-suggested to somebody.
The last person I knew to do so, did so knowing they were going about it the "hard way". They also did it with the intent of actively upgrading/updating the code WYASI48H as they went into a more modern style. They gave a talk on it after.
It's a good way to firm up your knowledge if it's the sort of project that interests you, but needs love and isn't a good introduction for most.
The course you recommend (it's good, I've read it) involves writing parsers and some bits of Risk; the follow-up includes another university course, LYAH, and RWH.
You also have a lot of good resources for Cabal and for development environments. That's appreciated.
But a lot of developers' bread and butter is "this data is here; I need to grab it from this place, make appropriate changes to it, and put it in this other place." They might use an HTTP client and a JSON parser for it, or FTP and an image processing library, a database or two, etc.
Unless I'm mistaken, the first place new Haskellers would run into the first inkling of that kind of stuff would be in chapter 5 of RWH, which you describe as a "thick book" and a "reference". The first appearance of a database is in chapter 20.
This is stuff that you can find about PHP, Ruby, Python, etc. by drooling into the keyboard while Google is open. It is comparatively _exceptionally_ hard to find the same kind of nuts-and-bolts material about Haskell.
I think this is an interesting point and it seems to me there are two distinct learning styles. Bottom up learning where you start from the basic features of the language and theoretical foundations of computers (or the practical foundations for C and assembly) and a top down approach where you start with rich frameworks and bolting together code that you don't understand.
I think the different approaches lead to a different environment in terms of libraries available and the types of learning guides available.
There's definitely a stage when you want to graduate from bottom-up tinkering & learning the underlying concepts to making things -- this is where I feel there's a big gap in the haskell materials right now. There's some great screncasts though which have been helpful.
Of course those starting at the bolting together of significant blobs may learn their way down to the more fundamental levels too as they start to dig into the libraries that they use and even down into the languages.
I don't take a particular view on which is the better way. The distance from the fundamentals up to something superficially impressive seems a very long way to me. So I think probably the realistic approach for most will be to start of the bolt things together and then discover a desire to understand the fundamentals.
I think to start at the fundamentals end needs a lot of patience but it probably suits those intolerant of magic.
I think to start at the fundamentals end needs a lot of patience but it probably suits those intolerant of magic.
Very well said; I'm one of those people and that's what drove me from Ruby to Haskell - Ruby and its frameworks are all very magical and don't seem to hinge on any common foundation other than the intricate details of Ruby's object model, which is not a satisfactory set of principles, but an arbitrary set of rules.
On the other hand, while Haskell is several times harder for it, it does eventually quench the thirst to connect every new thing I learn with the foundational things I already know (and which are in fact a set of simple rules - lambda calculus).
Haskell isn't like most languages people have used. You can't dive into the sort of things you're talking about unless you've used at least OCaml and even then there's going to be a fair bit that's unfamiliar.
It's more like learning to program again, not just a language.
The course I lay out is because it's what I've found to work best. Part of the priority is minimizing dropout rate.
Trying to shove people through a 'practical' project had one of the worst dropout rates of any of the approaches I tried.
Just accept that you're going to learn some basics before dazzling the world with your web app and you'll be much happier for it.
It doesn't take long anyway, if you have a well-designed course and/or experienced people helping you. [1]
I've been thinking about writing a book to address some of the pedagogical problems. I don't know if it'll work out, but I have a few ideas.
So far, the course I recommend not having enough "practical" examples has been pretty far down the list of problems. I'd rather focus on the rough edges that I've observed tripping people up.
No, I'm learning OCaml, although I've gone through the normal introductory Haskell material and liked it.
The catch in your approach is that it doesn't really offer an incentive for people to learn Haskell other than "it's really good, believe me". The best incentive I know of is to demonstrate that people are solving the problems that the potential Haskeller wants to solve, and that there are tools, resources, and even sample code available.
Just a little while ago there was a goddamn GIF encoder in JavaScript on the HN frontpage. There was a text editor in Go. Does anyone really need that stuff? Probably not. But the visibility of people solving so many familiar problems in these languages gives newcomers the impression that JavaScript/Go/whatever are powerful, popular languages suitable for a broad array of tasks.
Seeing that kind of thing -- that the Haskell community does more than write language parsers and terse implementations of interesting algorithms -- is what's going to convince a newcomer that it's worth the investment to start from scratch on a new, unfamiliar language. At that point they're going to be happy to learn about typeclasses and functors and monoids as stepping stones to the cool stuff they want to do (and in the process, maybe learn that they're more than stepping stones).
>The catch in your approach is that it doesn't really offer an incentive for people to learn Haskell other than "it's really good, believe me". The best incentive I know of is to demonstrate that people are solving the problems that the potential Haskeller wants to solve, and that there are tools, resources, and even sample code available.
I have been saturated and have had to scale how I teach for the last 9-12 months, so marketing hasn't been a first-order concern.
There are "ooh" and "ahh" demos I could put up, but other people are doing that work better than I could. I'm just the funnel catching people already sold.
Teaching is hard and isn't done well. I like working on this problem and would like to focus on it for the time being.
Here's a three-post series building a cellular automata that works on non-standard topologies along with a PNG encoder to print out the results. If I remember right it culminates in a live demo display being served by a Haskell web server.
I'm not sure about "new Haskellers" but when I have a "grab data from here, do something with it, maybe involving a database" I currently turn to conduits - typically by seeing if there's a conduit-* package on Hackage.
RWH is not where you should be turning for up-to-date info on library support for stuff - a lot of it is pretty out of date (which is not to say that there's not plenty of value to be learned there).
>The course you recommend (it's good, I've read it)
Sidebar to everybody else who might this thread: don't just read cis194, that's a terrible waste of time. You have to do the exercises, including the very first ones.
This is why I'm always tempted to drop non-comment prose. People just starting out think reading will do something for them.
While I agree that this is a problem in some sense, I also think it's a rather natural consequence of the primary strengths of Haskell: its focus on mathematical abstraction; its rational & precise language theory; and its willingness to impose a learning curve and do things differently.
The Haskell community has an inofficial motto that reads "avoid success at all costs," from an old presentation by Simon Peyton-Jones. The correct interpretation is "avoid (success at all costs)," that is, don't make decisions for "populistic" reasons, and don't be afraid to take the "high road."
Of course, it would be amazing to see much more "practically" oriented teaching material, because as you say there's quite a bit you just have to figure out. But I don't think Haskell is uniquely bad at this. It's just bad in different ways...
Documentation for dynamically typed, "pragmatic" things – like jQuery, many Node libraries, etc – often confuses and annoys me, because I'm so used to Haskell's type specifications and clear abstractions. Granted, I've been coding Haskell for years, and went to a university that uses it as the primary language for students, but I think Haskell libraries are often very clear and easy to use.
Haskell also has truly excellent frameworks and libraries for things like concurrency and parallellism. Simon Marlow's book is exemplary and wonderful; it should qualify as a very good resource for practicing developers.
Yet another point: GHC's type errors can seem obscure for a beginner, and sometimes they're indeed very tricky, but largely, the compiler actually provides loads of helpful information in a clear and well-presented way (searching for contextual info, spell checking, etc). I can't properly express how much I prefer fixing GHC type errors over debugging JavaScript type errors...
Honestly, I think the state of teaching programming is not great in general. I remember the Java courses at university... Learning how to use Swing, UML diagrams, sockets... It's kind of a nightmare.
But regardless of all of this, it's evident that more and more people are attracted to using Haskell for "real-world" systems, and I believe that Haskell's incubation period as an academic language was utterly crucial to its maturation as a novel and strong technology.
(I'm not accusing you of this by any means, but sometimes there's an anti-academic tone in criticisms of Haskell, which I think is completely misguided and frankly dangerous.)
Regarding the anti-academic tone: I hear you, but you know what they say about converts being the greatest fanatics? There's a certain loud category of people who have relatively recently achieved illumination with Haskell but have very little of use to tell anyone else about it.
Conversations with this type tend to go along the lines of "Haskell is the best"/"Why"/"Static typing, lazy evaluation, purity"/"Why are those good things"/"derisive snort". Usually "blub" gets tossed in there for good measure.
Agreed on the generally dismal state of programming education. Usually I'm pretty glad I didn't go to college -- those four years were better spent working on real projects with smart people. But I have been trying to backfill the interesting and useful academic material as I go.
I guess so. I got a bit fed up with parsing text too and got side-tracked to more graphical things. Initially to Elm [1].
It's not exactly Haskell but Elm is a fun way to get into Functional and Functional Reactive programming while drawing some things on screen and making them move.
There are probably more graphical tutorials for Haskell too. I recently found some good examples for Gloss [2] and am moving from Elm to that right now. There is an "Elm for Haskell" called HElm [3] too.
What about html parsing/posting, api creation/consumption, and screen scraping? It's what I've used Haskell for the most and would be happy to make some introductory tutorials.
Mostly for the advantages of purity and the trend of Haskell code I writing just being correct the first time. Also, with the help of dom-selector[0] it is very concise.
I'm also using wreq[1] recently which has a great intro tutorial and is very concise with the help of lenses[2]. Mind you, I only use the most basic lenses but that has been enough for me so far.
I look forward to trying taggy-lens[3] in the future which is a more lens based approach of parsing html. Though it's hard to imagine anything being more concise than the dom-selector approach.
To go back to your question again though, Haskell has enabled me to write mostly bug-free code quickly and there was more than enough library support for screen scraping. The biggest problem was trying to pick the tools/libraries to use for my task.
But I've found it semi-refreshing to be confronted with something that is different enough that I have to sit down with it regularly to practice. It isn't just another language, and it really isn't amenable to just fiddling with things until they work.
That said, the gap from newbie to competent is big, an I feel like this is mostly a documentation issue. I've only started very recently and I have awhile to go. You can't expect to grok the implications of monads, laziness, immutability, and pure functional style immediately. But hat doesn't mean they aren't worthwhile, or interesting.
Now if you'll excuse me, I need to harass, er, ask for help on some Haskell I'm stuck on. (BTW: the community is really helpful. There are no charlatans or self-promotional jerks like you'll find elsewhere.)
I think that's raart's point. The syntax is really simple and easy to pick up, so all the tutorials focusing mainly on syntax and paying less attention to actually, well, getting things done, do the language a disservice.
In nearly every mainstream programming language, you call a function like this:
f(a, b, c)
But not in Haskell, where you do it like this:
f a b c
Of course, there is a reason for doing things this way in Haskell, namely making currying a natural part of the language. If f is a function of three variables, f a b binds values to the first two parameters, yielding a function of one variable.
I've just started with Learn You A Haskell [0] and I'm really enjoying it (and learning). It does focus a lot on the syntax and types at the beginning though - but I've found that a nice way to learn so far.
I'd like to see some practical software written in Haskell (probably database based like you suggest). It sounds great theoretically, but I haven never come across Haskell in the wild.
After spending a day installing virtual machines and cabal sandboxes, my verdict:
Until Haskell ecosystem stabilizes and it is possible to install a complex (many dependencies) library/tool without hitting cabal-version hell, nothing much will get done in Haskell.
Highlights from my day, installing released packages on my Mac:
cabal: The following packages are likely to be broken by the reinstalls:
unordered-containers-0.2.4.0
case-insensitive-1.1.0.3
network-2.4.2.3
HTTP-4000.2.10
Use --force-reinstalls if you want to install anyway.
3.
[2 of 7] Compiling Language.Haskell.BuildWrapper.GHCStorage ( src/Language/Haskell/BuildWrapper/GHCStorage.hs,
dist/dist-sandbox-7b4470b7/build/Language/Haskell/BuildWrapper/GHCStorage.o )
src/Language/Haskell/BuildWrapper/GHCStorage.hs:305:32:
Not in scope: data constructor ‘MatchGroup’
4.
Resolving dependencies...
(searching the entire graph of dependencies, trying in vain for mny minutes, to find a set of package versions that are mutually compatible)
I was toying with Haskell a few years ago, and I found it unpractical.
Sometimes it turns out you need to do something with IO somewhere, and now you have to change all the functions down the way to use it.
Similar issue is lack of exceptions - Haskell just has a type enhanced version of 'return -1' from C. Exceptions should be completely separate from successful execution paths. Throwing exception from a pure function with type like Int -> Int requires changes in everything that uses it.
Common Lisp's condition system is much, much better.
The third issue I remember was that all fields' names are global.
All of this makes it very hard to write the minimal working code now and extend it later. Instead, you have to provide scaffolding for possible future changes, or risk having to change everything.
Maybe there's some True Way of using Haskell which doesn't suffer from these issues, but I haven't found it.
Unfortunately I found it really takes a lot of work on practical programs to get past the stage where you have these sort of doubts. Finding the motivation to do so can be a chicken-in-egg type of problem.
Technically you are incorrect about exceptions needing to change the type of pure functions, but in fact I would always prefer to return an Either and so it does result in code changes up the stack. However it may not mean very many changes to signatures; a function used by only one parent shouldn't be top-level and doesn't need a signature. And the result is a function that captures failure modes in its type, which means other consumers can't forget to handle failure (though they can explicitly choose not to if they want to just throw an exception on failure). This may feel a bit verbose and unwieldy at first but it actually is very powerful once you know the techniques.
The concern about IO - you don't say this but a lot of people worry about logging output to STDIO for debugging. But Debug.Trace will do this impurely for debugging purposes. Otherwise its actually pretty rare that you write a pure function that later turns out to need IO. IO should usually stay in top-level functions that provide the over-all program / routine structure. I'm only at a journeyman level with Haskell but I've written over 20,000 lines of real code and can't remember ever really running into this problem, though I did worry about it when just getting started.
Exceptions in Haskell simply are not the same as in other languages. In particular, they probably should only ever occur in IO code.
Why? Because right now you spend a lot of time trying to reason about non-local jumps in code due to exceptions. Purr Haskell code frees you from that concern by making everything local and explicit.
You have to learn a new style, but once you do going back to pervasive exceptions is a pain! It's really hard to trust code that might bubble up an exception at any point.
Finally, I've found that the times I genuinely want bubbling exceptions are exclusively IO scenarios anyway. Touching the real world is when you're likely to need to handle partial coverage of behavior amyay.
So it all matches up and is wonderful, but is also quite different from what you might normally expect. In particular, you need to learn how to think about and reason within pure code—once you learn that you'll find that predicting the right type signatures is easy. They just explain exactly what your function really does.
Exceptions in Haskell are separate from successful execution paths. It has `throw :: Exception e => e -> a`, which can be used in pure code. Only catching, i.e. handling exceptions, has to be in IO.
The IO issue you mention is a feature to me: it means your code will have a very clean structure with IO at the top level, and pure domain code further down. But perhaps others prefer other structures, I don't know.
Field names are not global. They are scoped the same way as everything else. They are in scope in the module, and you can manage visibility in other modules with exports and imports.
The "True Way" of using Haskell involves getting the type system to work for you.
It is true that as your project scope changes in surprising ways, occasionally you'll discover you need some facility somewhere that the types don't currently make it available. Yes, you've got to go through and re-plumb things - but your compiler will quickly point you at everything that needs to change to that end. As michaelochurch says, keep everything small and composable - and I would add "abstract", with some caveats - and much of it will shift automatically where it's safe to do so.
Edited to add:
Regarding the "update the types so that I can do something I couldn't" issue - it's not really any different than writing any language and discovering that you need some information you don't have handy. Threading it through the various function calls is not hard and usually the right thing to do. "I can do anything anywhere" is the equivalent of making it a global.
I have been on a four year journey for learning Haskell so I understand where you are coming from.
I have recently starting doing almost all of my NLP and semantic web/linked data experiments in Haskell and that has helped get me over a learning curve hump, a bit.
One thing that helps me, when possible, is to get the pure code working before worrying about network and file IO, etc. Working on pure functional code is really nice. Monads still cause me some grief and I look for examples on the web of what I need to do, and modify them.
I have given up the idea of ever becoming an expert in Haskell, but I am enjoying it and getting stuff done.
> Sometimes it turns out you need to do something with IO somewhere, and now you have to change all the functions down the way to use it.
You really shouldn't need that.
> Exceptions should be completely separate from successful execution paths.
Its easier to write code with a language that does that, but harder to analyze what existing code does and debug it. So, no, while I understand the initial attraction of that approach, I don't think you can validly say that the One True Way is the one Haskell doesn't use here.
> The third issue I remember was that all fields' names are global.
Well, like all functions, they are the top level of the module. But they ought to, outside of simple programs, be isolated in a module for the data type, which can then be imported qualified when used.
>Sometimes it turns out you need to do something with IO somewhere, and now you have to change all the functions down the way to use it.
Que no!
Bloodhound, which is expressly about talking to an outside database, has >500 type signatures. Fewer than 40 have IO in them. You most certainly do not need to add IO to pure code just because something in IO invokes it.
This is why we have things like Functor, monad transformers, etc.
Some of the responses to you are good and some aren't. Honestly I don't think the point here is to tell you what you don't know (which is a common strategy for Haskellers).
I will tell you this, though. In order for your mind to be productive in Haskell you have to unlearn a lot of imperative and executional ways of thinking. Haskell excels in describing what something is (through the type system, also lookup "denotational semantics" - reading up on that helped me understand what the character difference is between Haskell and the more imperative languages I was brought up on).
I definitely do think a language like Haskell has immense gains for the enjoyment of programming, business execution, and safety of the programs we write that the general public consumes.
Yea it's simple. Do not nest function calls when possible. Use the rich and widely available composition facilities of the language to compose functions instead of nest them. This will generally amount to a much cleaner design in the end anyways - in any language. But Haskell is one of the few languages that is abstract enough to give the ability to write these combinators.
Sometimes it turns out you need to do something with IO somewhere, and now you have to change all the functions down the way to use it. Similar issue is lack of exceptions - Haskell just has a type enhanced version of 'return -1' from C. Exceptions should be completely separate from successful execution paths. Throwing exception from a pure function with type like Int -> Int requires changes in everything that uses it.
That's the price of static typing. There are a lot of tricks to make that more bearable (monads and do-notation) but, you're right, refactoring requires some work until you make all the type signatures work.
Exceptions are a pain in Haskell. It's hard to get them to work in an intuitive way with laziness, and it's unclear how they'd interact with a type system. In an ideal world, Int -> Int would mean a function that can't throw an exception. Of course, there are very few people who want to live in a world where division has a type signature of Int -> Int -> Maybe Int, and it's not clear what else to do (but throw an exception, or hard-crash) in the case of unhandled division-by-zero.
Haskell has an exception system but it's hard to use because of laziness and some other factors. The more idiomatic error handling is to use the Either type:
Either l r = Left l | Right r
where Left corresponds to the erroneous case and Right to the successful path. Either is a monad in the latter argument so you can use do notation to make it "look imperative".
The third issue I remember was that all fields' names are global.
They're specific to the module you're writing in. If you import a whole module (which you generally shouldn't, except in ghci) then you'll often have collisions.
All of this makes it very hard to write the minimal working code now and extend it later.
I wouldn't necessarily use Haskell for a small project. Python or Clojure are great tools, and dynamic typing (IMO) is perfectly fine until you get into thousands of LoC. Of course, if you like static typing (which I do) and want to learn more about it, you can certainly use Haskell for small projects and it will work just fine... but it's more efficient (if you're not familiar with Haskell) to use Python or Clojure.
Maybe there's some True Way of using Haskell which doesn't suffer from these issues, but I haven't found it.
It took me a while to "get" the elegance of Haskell. It forces you to program in a different way: small functions, minimal interleaving of stateful computations (IO) and stateless ones. It can be very concise and beautiful, but it's very different from other languages, even other high-power ones like Clojure.
What I would like (and in my opinion what Haskell needs to grow), is a more practical introduction, like doing a database conversion, or writing some network service. Syntax can be explained in passing. Does this exist?