Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Haskell-cafe] learning advanced haskell

23 views
Skip to first unread message

Aran Donohue

unread,
Jun 14, 2010, 1:42:28 AM6/14/10
to haskel...@haskell.org
Hi Cafe,

I've been doing Haskell for a few months, and I've written some mid-sized
programs and many small ones. I've read lots of documentation and many
papers, but I'm having difficulty making the jump into some of the advanced
concepts I've read about.

How do people build intuitions for things like RankNTypes and arrows? (Is
the answer "Get a PhD in type theory?") Normally I learn by coding up little
exercise programs, but for these I don't have good intuitions about the
kinds of toy problems I ought to try to solve that would lead me to
understand these tools better.

For systems like Template Haskell and SYB, I have difficulty judging when I
should use Haskell's simpler built-in semantic abstractions like functions
and typeclasses and when I should look to these other mechanisms.

I understand the motivation for other concepts like iteratees, zippers and
functional reactive programming, but there seem to be few entry-level
resources. John Lato's recent Iteratee article is a notable exception*.

Hints? Tips? "Here's how I did it"? _______ is a great program to write to
get to learn ______?

Thanks in advance,
Aran

* Even in this article, he busted out (>=>). And it appears that the
iteratee library's actual design is more sophisticated than the design
presented.

Chris Eidhof

unread,
Jun 14, 2010, 4:33:03 AM6/14/10
to Aran Donohue, haskel...@haskell.org
On 14 jun 2010, at 07:42, Aran Donohue wrote:

> Hi Cafe,
>
> I've been doing Haskell for a few months, and I've written some mid-sized programs and many small ones. I've read lots of documentation and many papers, but I'm having difficulty making the jump into some of the advanced concepts I've read about.
>
> How do people build intuitions for things like RankNTypes and arrows? (Is the answer "Get a PhD in type theory?") Normally I learn by coding up little exercise programs, but for these I don't have good intuitions about the kinds of toy problems I ought to try to solve that would lead me to understand these tools better.

I learned my advanced stuff in two ways:

1. I followed excellent courses at Utrecht University, where they teach (among other things) advanced functional programming.
2. I just read a lot, which is a lot slower than 1.
3. By building my own programs, I inevitably find my self stuck and while searching for a solution, sometimes these concepts are the answer. For example, once I had a monadic program and I wanted to serialize/inspect the program, which turned out to be impossible in my case. The solution was to write it down using arrows (thus I had to learn arrows and also arrow-notatation). I think (for me) this is the most powerful way to learn new advanced concepts. I need practical problems to keep myself motivated.

> For systems like Template Haskell and SYB, I have difficulty judging when I should use Haskell's simpler built-in semantic abstractions like functions and typeclasses and when I should look to these other mechanisms.

Sometimes Template Haskell or SYB is the answer, and it's a matter of style, but I try to avoid them as much as possible. I find that generic programming in the style of regular [1] or emgm [2] is often much simpler.

Some more tips: subscribe to planet haskell, if you haven't done that already. Try to find ICFP/JFP/etc papers online that interest you.

-chris

[1]: http://hackage.haskell.org/package/regular
[2]: http://hackage.haskell.org/package/emgm_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Ivan Lazar Miljenovic

unread,
Jun 14, 2010, 4:39:37 AM6/14/10
to Aran Donohue, haskel...@haskell.org
Aran Donohue <aran.d...@gmail.com> writes:

> I've been doing Haskell for a few months, and I've written some mid-sized
> programs and many small ones. I've read lots of documentation and many
> papers, but I'm having difficulty making the jump into some of the advanced
> concepts I've read about.
>
> How do people build intuitions for things like RankNTypes and arrows?

By being told that using them would solve some problem you're
complaining about on #haskell or the mailing lists, you look at
examples, read up on them, etc.

Short version: don't worry about advanced concepts until you have to.
If all else fails, it doesn't hurt to write out the low-level version
yourself and then get told in a code review that it would be "easier" or
more elegant with an advanced technique.

--
Ivan Lazar Miljenovic
Ivan.Mi...@gmail.com
IvanMiljenovic.wordpress.com

Neil Brown

unread,
Jun 14, 2010, 5:54:04 AM6/14/10
to Aran Donohue, haskel...@haskell.org
On 14/06/10 06:42, Aran Donohue wrote:
> Hi Cafe,
>
> I've been doing Haskell for a few months, and I've written some
> mid-sized programs and many small ones. I've read lots of
> documentation and many papers, but I'm having difficulty making the
> jump into some of the advanced concepts I've read about.
>
> How do people build intuitions for things like RankNTypes and arrows?
> (Is the answer "Get a PhD in type theory?") Normally I learn by coding
> up little exercise programs, but for these I don't have good
> intuitions about the kinds of toy problems I ought to try to solve
> that would lead me to understand these tools better.
>
> For systems like Template Haskell and SYB, I have difficulty judging
> when I should use Haskell's simpler built-in semantic abstractions
> like functions and typeclasses and when I should look to these other
> mechanisms.
>
I'd second Ivan's suggestion to "learn-by-need". For SYB and Template
Haskell, and probably for most other fancy abstractions, you can get on
happily without them in most cases. When you start to think "there must
be a better way!" while coding, that's when you should start looking for
advanced features.

For example, if you find yourself writing the Nth boilerplate function
that pattern-matches all cases in your ADT just to apply a function in
its sub-types, that's when you'll want some form of generic programming
like SYB. And by that point you'll understand one of the problems that
generic programming solves, which is halfway towards understanding the
techniques themselves.

Hope that helps,

Neil.

Martijn van Steenbergen

unread,
Jun 14, 2010, 9:46:12 AM6/14/10
to Ivan Lazar Miljenovic, haskel...@haskell.org
On 6/14/10 10:39, Ivan Lazar Miljenovic wrote:
> By being told that using them would solve some problem you're
> complaining about on #haskell or the mailing lists, you look at
> examples, read up on them, etc.
>
> Short version: don't worry about advanced concepts until you have to.
> If all else fails, it doesn't hurt to write out the low-level version
> yourself and then get told in a code review that it would be "easier" or
> more elegant with an advanced technique.

Exactly this. It's happened a few times now that I ran into a problem
and then a bit later found out that feature XYZ was exactly what I
needed. A feature I never understood but now suddenly had a good
intuition for because it is a (or the) solution to a problem I had been
thinking about for a while.

But sometimes a feature looks really interesting and you really want to
run into a problem to which the feature is the solution. If this is the
case for you with RankNTypes, here is such a problem:

1) What's a type of this function? I say *a* type because there are
multiple correct answers.

> debugWith f = do
> putStrLn (f True)
> putStrLn (f 'c')

Don't ask the compiler to infer the type for you; it won't be able to.
One of the characteristics of RankNTypes is that the compiler needs you,
the programmer, to supply the type.

2) What would be a good argument to debugWith?

3) What's one reason the compiler can't infer the type for you?

Hope this helps!

Martijn.

Ivan Lazar Miljenovic

unread,
Jun 14, 2010, 9:57:17 AM6/14/10
to Martijn van Steenbergen, haskel...@haskell.org
Martijn van Steenbergen <mar...@van.steenbergen.nl> writes:
>
> 1) What's a type of this function? I say *a* type because there are
> multiple correct answers.
>
>> debugWith f = do
>> putStrLn (f True)
>> putStrLn (f 'c')
>
> Don't ask the compiler to infer the type for you; it won't be able
> to. One of the characteristics of RankNTypes is that the compiler
> needs you, the programmer, to supply the type.

I randomly guess something like "(forall a. a -> String) -> IO ()" (but
haven't done anything myself with existentials so I'm quite possibly
wrong).

> 2) What would be a good argument to debugWith?

Does "show" suffice?

> 3) What's one reason the compiler can't infer the type for you?

foralls are tricksy little devils? :p

John Lato

unread,
Jun 14, 2010, 12:05:01 PM6/14/10
to Aran Donohue, haskel...@haskell.org
> From: Aran Donohue <aran.d...@gmail.com>

>
> Hi Cafe,
>
> I've been doing Haskell for a few months, and I've written some mid-sized
> programs and many small ones. I've read lots of documentation and many
> papers, but I'm having difficulty making the jump into some of the advanced
> concepts I've read about.
>
> How do people build intuitions for things like RankNTypes and arrows? (Is
> the answer "Get a PhD in type theory?") Normally I learn by coding up little
> exercise programs, but for these I don't have good intuitions about the
> kinds of toy problems I ought to try to solve that would lead me to
> understand these tools better.

I read as much as I can. On the cafe, Planet Haskell, papers,
whatever. If I don't understand a paper, I leave it be for a while
then come back to it in a few weeks/months. Usually I'll be able to
make further headway.

Also don't try to read too much at once. The literature is quite
dense and I find it useful to let things sit and percolate for a
while.

For the specific cases of RankNTypes and arrows I had nearly opposite
experiences. I understood RankNTypes immediately, in that I expected
higher-rank behavior and was surprised when code didn't work that way.
Arrows were particularly difficult for me (for much the same reason
as the Monoid class, incidentally). I understood the concept of
arrows, but was unable to figure out how to actually *do* anything
with them for some time. I'm still not sure that I have a great idea
of when arrows are useful as a computational abstraction; they seem to
be used more for the notation and convenience functions than anything
else.

> I understand the motivation for other concepts like iteratees, zippers and
> functional reactive programming, but there seem to be few entry-level
> resources. John Lato's recent Iteratee article is a notable exception*.

This is exactly what I was aiming for, so I really appreciate hearing
your comments. I'm a beginner in many areas as well, so I also would
like to see more entry-level resources.

>
> Hints? Tips? "Here's how I did it"? _______ is a great program to write to
> get to learn ______?
>

Stuff I have a decent understanding of:

RankNTypes - Look at this function

> makeString :: (Show b, Show c) => (a -> a) -> b -> c -> String
> makeString f b c = show (f b) ++ show (f c)

If you try to call it as e.g.

> makeString id 0 "foo"

GHC provides a type error. This doesn't work because when GHC unifies
the types, it does something like this:

1) the first argument, f, is a function with type a -> a
2) the function f is applied to an Integer, so f :: Integer -> Integer
3) the function f is applied to a String, so f :: String -> String
4) there are conflicting types for f, so bail out

Even though you declare f :: a -> a, that 'a' must be set to exactly
*one* value within the call, where makeString tries to apply it to
values with different types. This is rank-1 polymorphism. RankNTypes
fixes this by allowing us to specify that the "f" function is supposed
to be polymorphic within the makeString function (rank-2 polymorphism)
as opposed to the standard rank-1 polymorphism:

> makeString2 :: (Show b, Show c) => (forall a. a -> a) -> b -> c -> String

calling makeString2 does the right thing. That's pretty much the
whole story of Rank2Polymorphism. RankNPolymorphism is exactly the
same with nested levels of forall's.

One really useful feature of this is that it provides a sort of
inversion-of-control for polymorphism. Typically the caller has the
ability to define the types when a function is called, as happens with
the b and c parameters to makeString2. But in the first argument the
*callee* determines the type values of the function! The caller knows
only that the function must take one argument and return a result of
the same type (which also means "id" is the only interesting function
of the appropriate type). The callee is free to instantiate the given
function with *any types it wants*. This is also what makes the ST
monad safe incidentally.

Arrows - Toy project: write a small program and use arrow notation to
combine functions. Write the zipWith version of the "fib" function
with arrows. Most of the stuff on the web is pretty readable IMHO.
Yampa is a good place to move beyond "Arrow" and into "ArrowLoop".

Zippers - Toy project: anything that keeps track of decisions in a
tree. Mapping a path as in Theseus and the Zipper seems good. I
learned it by implementing a user history function.

Template Haskell - Toy project: programatically generating data
declarations seems like a good starting point because it's relatively
simple and can't be done with normal code. Instances are a good
follow-up. Of course this is only a small portion of what TH can do,
but it's a start. Bulat's tutorials are the best on this topic I'm
aware of.

I left out a lot of stuff that I'm still working with myself (SYB,
Finally Tagless, category-extras, ...).

> Thanks in advance,
> Aran
>
> * Even in this article, he busted out (>=>). And it appears that the
> iteratee library's actual design is more sophisticated than the design
> presented.

Well, yes. If the library implementation (or Oleg's examples) were
the same as the implementation I presented, there probably would have
been no need for the article!

John

Stephen Tetley

unread,
Jun 14, 2010, 1:25:18 PM6/14/10
to Patrick LeBoutillier, haskel...@haskell.org
On 14 June 2010 18:00, Patrick LeBoutillier
<patrick.le...@gmail.com> wrote:
> Hi all,

>
>
> On Mon, Jun 14, 2010 at 1:42 AM, Aran Donohue <aran.d...@gmail.com> wrote:
>
>> resources. John Lato's recent Iteratee article is a notable exception*.
>
> Can anyone provide a link to the article (if it's available online)?
>

It was in the last issue of the Monad Reader...

http://themonadreader.wordpress.com/
http://themonadreader.files.wordpress.com/2010/05/issue16.pdf

Also Gerard Huet's original Zipper paper is a very clear read, the
paper seems to be on CiteSeer.

John Lato

unread,
Jun 14, 2010, 2:39:24 PM6/14/10
to haskel...@haskell.org
> From: Martijn van Steenbergen <mar...@van.steenbergen.nl>

>
> On 6/14/10 10:39, Ivan Lazar Miljenovic wrote:
>> By being told that using them would solve some problem you're
>> complaining about on #haskell or the mailing lists, you look at
>> examples, read up on them, etc.
>>
>> Short version: don't worry about advanced concepts until you have to.
>> If all else fails, it doesn't hurt to write out the low-level version
>> yourself and then get told in a code review that it would be "easier" or
>> more elegant with an advanced technique.
>
> Exactly this. It's happened a few times now that I ran into a problem
> and then a bit later found out that feature XYZ was exactly what I
> needed. A feature I never understood but now suddenly had a good
> intuition for because it is a (or the) solution to a problem I had been
> thinking about for a while.

I sort of agree with this, with some very large caveats. I would
agree that being faced with an actual problem provides a particularly
good learning environment for several reasons. For the "pragmatic
programmer", this is probably the way to go.

However, there's a lot to be said for both intellectual curiosity and
learning for the sake of knowledge. Nearly all of us specialize in
certain problem domains or areas and will likely never work on certain
problems (related to e.g. compilers, web servers, etc.) in anything
like a commercial environment. Some of us may never work in a
commercial code environment at all. Just because you may never need
to use a feature doesn't mean you shouldn't be able to understand it.

John

John Lato

unread,
Jun 14, 2010, 2:45:19 PM6/14/10
to haskel...@haskell.org
> From: Neil Brown <nc...@kent.ac.uk>

>
> For example, if you find yourself writing the Nth boilerplate function
> that pattern-matches all cases in your ADT just to apply a function in
> its sub-types, that's when you'll want some form of generic programming
> like SYB.  And by that point you'll understand one of the problems that
> generic programming solves, which is halfway towards understanding the
> techniques themselves.

This is probably the most approachable description of generic
programming I've seen. Every generic framework should have a
paragraph like this on the front page of the docs.

Andrew Coppin

unread,
Jun 14, 2010, 3:33:49 PM6/14/10
to haskel...@haskell.org
John Lato wrote:
> I sort of agree with this, with some very large caveats.
>

Well, yes. If you don't know what a feature does, then you won't know
that it solves the problem you have.

> However, there's a lot to be said for both intellectual curiosity and

> learning for the sake of knowledge. Just because you may never need


> to use a feature doesn't mean you shouldn't be able to understand it.
>

There is that. However, in my experience, most of the advanced
techniques tend to be described in language beyond my comprehension.
(And most examples seem overly complex - although maybe that's just a
reflection of the fact that simple problems don't require sophisticated
techniques in the first place.) Having a specific problem to solve can
be quite helpful. Unlike an example, you already understand what the
problem is, and why it can't easily be solved any other way.

Aran Donohue

unread,
Jun 15, 2010, 4:18:53 AM6/15/10
to Andrew Coppin, haskel...@haskell.org
Thanks for the great responses. My haskell-learning todo list is refreshed
and renewed :)

I would point out, though, that had I followed a "Learn when needed"
philosophy more broadly I would never have come to Haskell or even
functional programming in general.

Aran

John Lato

unread,
Jun 15, 2010, 5:09:15 AM6/15/10
to haskel...@haskell.org
> Message: 7
> Date: Mon, 14 Jun 2010 20:33:30 +0100
> From: Andrew Coppin <andrew...@btinternet.com>
> Subject: Re: [Haskell-cafe] learning advanced haskell
> To: haskel...@haskell.org
> Message-ID: <4C16840A...@btinternet.com>
> Content-Type: text/plain; charset=ISO-8859-1; format=flowed

>
> John Lato wrote:
>
>> However, there's a lot to be said for both intellectual curiosity and
>> learning for the sake of knowledge. Just because you may never need
>> to use a feature doesn't mean you shouldn't be able to understand it.
>
> There is that. However, in my experience, most of the advanced
> techniques tend to be described in language beyond my comprehension.
> (And most examples seem overly complex - although maybe that's just a
> reflection of the fact that simple problems don't require sophisticated
> techniques in the first place.) Having a specific problem to solve can
> be quite helpful. Unlike an example, you already understand what the
> problem is, and why it can't easily be solved any other way.

Yes, having a specific problem to solve can be very helpful. The OP
requested examples of them. I'd like to see them myself, partially as
a reference but mostly to satisfy my own curiosity. More examples
please!

John

Bas van Dijk

unread,
Jun 15, 2010, 4:09:35 PM6/15/10
to Aran Donohue, haskel...@haskell.org
On Mon, Jun 14, 2010 at 7:42 AM, Aran Donohue <aran.d...@gmail.com> wrote:
> Hints? Tips?

One thing that isn't mentioned yet is to read other peoples programs.

I'm subscribed to the Hackage RSS feed[1]. I tend to read (at least)
the package page of every package that gets uploaded to hackage.
Whenever an interesting package comes a long I dig a little deeper.
First I try to understand what problem the package is trying to solve.
Then I see how the package is structured into modules. Finally when I
notice some interesting module or function I read its documentation
(which unfortunately almost never exists) and source code and try to
understand how and why it is being implemented like it is.

Regards,

Bas

[1] http://hackage.haskell.org/packages/archive/recent.rss

0 new messages