
Learning Haskell – A Racket programmer's foray into the land of Haskell - lexi-lambda
http://lexi-lambda.github.io/learning-haskell/
======
cakoose
The author spends a lot of effort trying to make his functions point-free. For
example, the author first writes:

    
    
        inexactMatches xs ys = matches xs ys - exactMatches xs ys
    

then decides it's not very "nice", and ends up with this:

    
    
        inexactMatches = pure2 (-) <**> matches <**> exactMatches
    

I can sympathize with this preference. When I first started learning Haskell,
figuring out how to make something point-free was extremely satisfying. And it
also increases DRY, which is generally a good thing.

But over time I realized I was overusing point-free style. It was fun to
write, but harder to read. If I were actually writing "inexactMatches" for
something real, I would strongly prefer the first over the second. From what
I've read, I think many people take a similar path when learning Haskell.

That said, I think everyone has a different threshold. For example, I've heard
many people say that a for loop is more clear than something like "map" or
"fold". I would disagree with that, but maybe that's just because I've gotten
used to "map" and "fold".

So maybe the author's brain is just able to handle point-free style better
than mine can, but I do think this particular example if far enough on the
spectrum that most Haskell programmers would prefer the first version (for
real code).

So if you're considering learning Haskell, don't be scared away. You will have
to start getting used to point-free style, but probably not to this extreme.

~~~
lexi-lambda
You're going to spend all that work using "the author" everywhere and still
manage to get my gender wrong ("his")? Okay. I don't really expect any
different in this field, but just noting that.

Anyway, I'm well-aware of the temptation of writing point-free code. I did
include the sentence "I can see the road to hell is paved with point-free
style" myself, after all. I wouldn't prefer point-free style if it ends up
looking indiscernible, but in this case I sort of wanted a way to express
semi-declaratively "this function is the difference between these other two
functions". Does the version I ended up with do that? Yes. But for all I knew
there could have been a much better, more idiomatic way.

But yes, that was both a quest to improve that code and a mere learning
exercise. I'm satisfied with the version I ended up with, though I don't think
it's perfect, either.

~~~
bigtunacan
If your first focus is on the usage of "his" in the response then maybe the
next language you should consider studying is English.

For starters you might want to read up on gender neutral pronouns. He/She or
his/hers can be used in a gender neutral manner, however traditionally the
masculine form is used in English when gender neutral is intended.

~~~
MBlume
Wow, that's some extraordinary condescension.

They/them/their has been seeing broad adoption as a, y'know, _actually_
gender-neutral pronoun, and it should be encouraged. I know some people prefer
he/him/his but

a) many people think that reeks of implicit sexism

b) even ignoring a), elevating that to The One Correct English Way is
ridiculous overreach

~~~
sgrove
Meh, I like 'hen'[0] a lot, wouldn't mind seeing a concerted effort to
introduce it in English as well. I doubt it would go over well in the states,
however.

[0]
[http://en.wikipedia.org/wiki/Hen_%28pronoun%29](http://en.wikipedia.org/wiki/Hen_%28pronoun%29)

~~~
evincarofautumn
Unfortunately, pronouns are a closed class[1] in English; the possibility of
getting people to adopt a new one is vanishingly unlikely. Fortunately, we
already have “they”, which has been used as a gender-neutral pronoun in the
singular for at least half a millennium.

[1]:
[https://en.wikipedia.org/wiki/Part_of_speech#Open_and_closed...](https://en.wikipedia.org/wiki/Part_of_speech#Open_and_closed_classes)

~~~
sgrove
Interesting, hadn't heard about closed/open classes in speech. I agree that
"they" serves the purpose well, but it's nice to see an explicit,
consciousness-raising fix (though it's the ultimate bikeshed politically,
everyone feels they're qualified to comment - even me!)

------
throwaway183839
> It feels a little redundant to have to manually list out all the elements
> when the information is known statically, and updating the list will be
> necessary if the Peg type changes. In Racket, writing a macro for this would
> be trivial, but alas, this is Haskell. Perhaps there is a way, I’m just not
> aware of it.

A reasonably clean way would be to define

    
    
      data Color = Red | Green | Blue | Yellow | Orange | Purple
        deriving (Enum, Bounded, Show)
    

and then (e.g. in ghci) you can do

    
    
      >> [minBound .. maxBound] :: [Color]
      [Red,Green,Blue,Yellow,Orange,Purple]
    

I often define the useful function

    
    
      enumerate :: (Enum a, Bounded a) => [a]
      enumerate = [minBound .. maxBound]
    

for exactly this purpose.

~~~
lexi-lambda
Yep, I posted this on r/haskell and someone pointed this out, which is cool.
The provided type for Peg didn't derive Enum or Bounded, but it's a good trick
to know.

~~~
throwaway183839
I'm kind of surprised that `enumerate` isn't defined in any standard libraries
(as far as I know) since I need it pretty often.

After a while spent programming in Haskell you would probably develop your own
mini-library of functions that make your life easier. Another one I often find
useful is

    
    
      (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
      (.:) f g = \x y -> f (g x y)
    

which e.g. allows you to define the absolute distance function

    
    
      dist :: (Num a) => a -> a -> a
      dist = abs .: (-)
    

A fun challenge is figuring out why the definition of (.:) is equivalent to

    
    
      (.:) = fmap . fmap
    
    !

~~~
JadeNB
> A fun challenge is figuring out why the definition of (.:) is equivalent to

> (.:) = fmap . fmap

> !

Of course it's not really _equivalent_ , since the most general type of the
LHS is as you have said, whereas that of the RHS involves functor constraints;
but inlining the definition of `fmap` for arrows gives the amusing definition:

    
    
        (.:) = (.) . (.)
    

I tend to figure this sort of thing out by successive eta conversion:

    
    
        \f g x y -> f (g x y)
        \f g x y -> (f . g x) y
        \f g x   -> f . g x
        \f g x   -> ((f .) . g) x
        \f g     -> (f .) . g
        \f g     -> ((f .) .) g
        \f       -> ((f .) .)
        \f       -> (.) (f .)
        \f       -> (.) ((.) f)
        \f       -> ((.) . (.)) f
                    (.) . (.)
    

Is there a better way?

------
bojo
Great read. I've finally taken the plunge and have spent the last 3 weeks
extensively studying Haskell in the evenings. My first attempt was a couple
years ago, but somewhere between then and now I've grokked something
mysterious and it's been easier to dig into. The last 6 months or so has been
something like Common Lisp -> Racket -> Haskell, which may have contributed a
little.

My long term goal is to write a realtime game server with it, however, I keep
flopping back and forth between wrapping my head around the high learning
curve and jumping to something more familiar to Get Things Done. It seems the
payoff for learning Haskell is intrinsic in and of itself even if I never end
up using it, yet I can't help but wonder if I'm spending my time wisely.

------
codygman
This was a great read, I'll be passing it along to anyone wanting to learn
Haskell so they can get a good feel for what it's like.

You had some advantages though since you've used Racket in the past.

I feel like your Racket experience helped you draw conclusions that many
wouldn't have otherwise. Great work though, I hope you write about more of
your experiences.

~~~
lexi-lambda
I'm curious what conclusions you'd say those are... and if they're good or bad
conclusions?

That said, I'm sure I'm totally influenced by various languages I've tried in
the past. And FWIW, Racket was also plenty far from my first experience with
functional programming. I know so many programming languages that I've gotten
pretty used to being able to just "pick them up" in a couple of days (Racket
pretty much included).

Haskell... not so much.

~~~
codygman
A little tired, but here are a few things that gave you a very _big_ advantage
in your Haskell endeavour IMO.

\- Knowing to start from scratch and assuming it's some install issue with
ghc-mod (this probably saved you a _lot_ of time) \- already knowing partial
application and that you can use backticks to take advantage of it in Haskell
\- just knowing what pointfree means \- knowing about hoogle \- knowing how to
search hoogle and what type of function you needed \- having an interest in
the asthetics of your code leading to experimentation that seems pointless but
teaches you a lot \- having confidence/knowledge of being able to just plugin
functions/types.

As far as conclusions, I'm mostly commenting on the conclusions you simply
made from the types... what people sometimes call "type tetris". Knowing you
needed `(a, a) -> Bool`, searching hoogle, then coming to the conclusion of
I'll implement it myself and moving on.

For a while it seemed like you were going to get caught in "point free hell"
and give up on your task... something I've sadly done in the past.

Then knowing to ask for help in #haskell and /r/haskell really helped you.

I know I haven't really answered your question of "what conclusions you'd say
they are"... what I was really trying to say is you made a lot of conceptual
leaps I wouldn't expect someone with very little Haskell experience to make.

If I remember I'll try to come back to this tomorrow and look for a few
examples of those conceptual leaps.

------
nathankleyn
I really enjoyed reading what you've written so far, and I'm hoping you
continue to write more.

It was really refreshing to see somebody write about the struggle to pick up
Haskell - indeed, I had a lot of the same issues. I often feel the Haskell
community is burdened with this stigma of "if you have to ask questions,
you're probably too stupid to get it". This is despite the fact that there is
actually a really great group of people always willing to help in #haskell.
It's primarily an image problem, one a lot of FP languages suffer from.

We need more articles talking about the pain points of Haskell so we can get
better at making this stuff accessible. We need to be honest with new
starters, tell them it's okay to find this stuff challenging and show them
where to find help. More than that, though, we need to think about what we can
do in the language to fix some of these pain points in a way that the
community is happy with.

------
ics
Looking forward to reading this too. For the opposite trajectory (Haskell
towards Racket) see this page from a little while back:
[http://artyom.me/learning-racket-1](http://artyom.me/learning-racket-1)

~~~
lexi-lambda
Yep, that was the inspiration for this. :) I actually linked to it in the
short intro bit at the beginning.

------
losvedir
Aha! I thoroughly enjoyed reading all that, and do hope you continue. It
sounds like you're at or slightly ahead of my level of Haskell experience (I
knew you'd need Eq before you did! But then the Applicative stuff was beyond
me...), so this is very insightful for me as I wonder whether to try picking
it up again.

"Fine. It's good enough. Moving On." made me laugh! It seems like you thought
that could be refactored in a much simpler way. Not knowing (much) Racket, it
didn't appear super obvious to me how. Were you alluding to a macro or
something? How would you do it in Racket?

~~~
lexi-lambda
Ah, no, I probably wouldn't do it any simpler in Racket. Racket tends to de-
emphasize more complicated function composition, which tends to serve it just
fine. I've always adopted a somewhat pro-functional approach in my usage of
the language, but even here there wouldn't really be a distinctly better way
to do it.

I guess I was just hoping that Haskell, being a more composition-oriented
language in many ways, would have some nice built-in to do that for me.

~~~
evincarofautumn
In concatenative programming languages, composition is the default, and it
makes point-free code much easier to write. In Factor, for example, you could
write your example as:

    
    
        : inexact-matches ( x y -- n )
          [ matches ] [ exact-matches ] 2bi - ;
    

That is, run both “matches” and “exact-matches” on both inputs, then take the
difference of the results. If Haskell had functions of multiple parameters and
results, 2bi’s type would look like this:

    
    
        (a, b, ((a, b) -> c), ((a, b) -> d)) -> (c, d)

------
mayisnotacake
That was an interesting read for me, because I started off with How to Design
Programs + Racket, before switching to Haskell.

(I would count Haskell as my first 'proper' language, and prior to all this, I
had zero coding experience.)

I experienced a lot of the same detours that you have gone through.

re: point-free code

There's a plug in that you can use, called pointfree (cabal install
pointfree). I found that it was more useful to me for experimenting with
point-free style than writing my own code in that way.

------
kamweti
From a PHP background, I've also been trying out haskell in the evenings, my
guide being the learn you a haskell book[1]. So far so good, I've started a
little blog[2] to clarify bits that sort of seem confusing to me.

[1] [http://learnyouahaskell.com](http://learnyouahaskell.com) [2]
[http://haskellrecipes.tumblr.com](http://haskellrecipes.tumblr.com)

------
iopq
Yeah, that feel when Cabal has the wrong version of the dependencies...

