

Implementing 2048 in 90 lines of Haskell - gu
http://gregorulm.com/2048-in-90-lines-haskell/

======
taylorfausak
Neat! I also cloned 2048 in Haskell [1]. I don't know how many lines of code
it is, but the core game logic is remarkably simple.

[1]: [http://taylor.fausak.me/2014/04/28/cloning-2048-in-
haskell/](http://taylor.fausak.me/2014/04/28/cloning-2048-in-haskell/)

~~~
jabagawee
I noticed that your blog post differed from the actual code in your git
repository. In the blog post, you defined `shift` to be:

    
    
       shift v = pad
         (map Just (concatMap add (group (catMaybes v)))))
         Nothing
         (length v)
    

whereas the code gives:

    
    
       shift v = take n (v' <> empty n)
         where
           n = length v
           v' = group (filter isJust v) >>= go
           go (Just a : Just b : ts) = Just (a + b) : go ts
           go ts = ts
    

From my understanding of the blog post's logic, the former doesn't handle the
case where `v = replicate 4 (Just 2)`, since it returns `[Just 4, Just 2, Just
2, Nothing]`. Am I correct, and if so, why does the latter version fix this
problem? For reference, I know little to no Haskell.

~~~
taylorfausak
Wow, excellent catch! I apologize for the discrepancy. I must have made a
mistake simplifying the game code for the blog post. I created a Gist [1] to
showcase the differences.

The problem in the blog post is the `add` function. It should recurse (like
`go` in the Hs2048 package). I fixed the post [2] with this line:

    
    
        add (x : y : rest) = x + y : add rest
    

[1]:
[https://gist.github.com/tfausak/4401ef0b43b5c1db0570](https://gist.github.com/tfausak/4401ef0b43b5c1db0570)
[2]:
[https://github.com/tfausak/tfausak.github.io/issues/31](https://github.com/tfausak/tfausak.github.io/issues/31)

~~~
jabagawee
Oh! So `go` is a function that's defined within `shift`. That's the crucial
part I was missing. Why is it named "go"?

EDIT: While I have your attention, do you mind also pointing me to some
resources to learn about this ">>=" operator? I recall it's something related
to monads...

~~~
taylorfausak
As ibotty said, I named the anonymous inner worker function `go`. From what I
can tell, it seems to be pretty common in Haskell.

As for `>>=` (also known as `bind`), I'm using it as a more generic version of
`concatMap`. The following expressions are basically the same:

    
    
        concat (map (\ x -> [x, -x]) [1, 2, 3])
        concatMap (\ x -> [x, -x]) [1, 2, 3]
        [1, 2, 3] >>= \ x -> [x, -x]
    

If you want to learn more, check out LYAH [1].

[1]: [http://learnyouahaskell.com/a-fistful-of-
monads](http://learnyouahaskell.com/a-fistful-of-monads)

------
leoh
Question: I've seen at least several succinct versions of programs written in
Haskell. Would people say this is a benefit of Haskell? Just skill on behalf
of the coder?

~~~
gu
One of the benefits of Haskell is indeed that this language makes it possible
to write very expressive code. The same is true for other functional languages
as well. However, this won't happen automatically. In other words, it will
take some practice. You can certainly write convoluted code in Haskell.

~~~
vamega
Yes, one of the worst mistakes I've seen people make is trying to write
Haskell exactly like you write code in other languages like Python.

If you ignore the power of Haskell's type system, and write all your code
imperatively in the IO Monad, and continue to write large functions that do
many things instead of writing smaller functions that can be composed, you
gain almost nothing from Haskell, and you might have been better off writing
your code in another language.

~~~
freyrs3
> you gain almost nothing from Haskell, and you might have been better off
> writing your code in another language.

Don't know about that. Even if you write everything in the IO monad, Haskell
is still a pretty great imperative language and many people say it may
possibly one of the best imperative languages ever written.

If you only use IO, then yes, you no longer gain the ability to reify and
isolate effects as values but all the other strengths of Haskell's type system
mostly remain invariant and just as useful.

------
taeric
This is neat and all, but the smooth animations of the original are probably
not something you can easily pull off in few lines of code. And that is a very
large fraction of the original, if I recall.

~~~
gu
The smooth animations in Gabriele Cirulli's version are due to this API:
[https://developer.mozilla.org/en/docs/Web/API/window.request...](https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame)
They consist of a few lines of code.

Further, the animation code is only a miniscule fraction of that code base.

~~~
taeric
Not entirely sure why you are getting voted down. (Well, unless you are
wrong...) :(

I'll have to dig through the code again. The polish that the end product of
the first version exhibited is well beyond that of any of the rewrites I've
seen. To the point that it is, in fact, jarring. And makes it frustrating to
read how much nicer the code looks in some of them.

Really brings home the point that intrinsic quality is nice, but pails in
comparison to the final quality of the overall product. It isn't that I am
convinced the sausage making process is always gross. Just that I have not
seen that many examples where the clean versions are anything more than just
sterilizations that have killed a lot of the original.

~~~
gu
Who knows, maybe my comment made some "full stack" developers feel insecure.
Just look at the code base of the original, and you'll see that the animation
is done through API calls.

Regarding the point you brought up: you are conflating two issues. One is
whether the UI is appealing, and the other whether the underlying code is of a
high quality. The point of my article was merely to show how nicely the game
logic can be expressed in Haskell, compared to many other languages.

~~~
taeric
I'm not necessarily conflating them, though. That is, can you, through
interaction with the original app, show that the code is of "low quality"?
Even relaxing it, can you show it is of "lower quality" than the Haskel
solution?

Because, just using the app, I'd say the original app is _much_ higher quality
than this derivative. One I would consider playing for a bit, the other is
just kind of neat.

If we are just going off how nicely the logic can be expressed, with no regard
to the dirtiness of implementation, free text wins. A simple paragraph
describing the game is much more understandable than even the haskel.
Especially if you allow examples.

Edit: Also, I'm curious if you could reproduce the UI of the original with
just a library in Haskel. That is, sure, most of the code is done though a
library. Does that really change much? If so, you should be able to achieve
the same fluid and pleasant UI with "just a library" in Haskel, right?
(Genuine question.)

~~~
gu
I think we're completely talking past each other. Yes, you can express the
game logic succinctly in Haskell. Yes, you will need many more lines in Python
or JavaScript. Anything else is conjecture on your part. "Free text" is not
executable, so your claim does not seem to be particularly relevant.

Feel free to use the UI as the sole measure of quality, but if that is your
stance, you should probably seek a different audience.

~~~
taeric
I'm not entirely sure you would necessarily need more lines of code to express
it in python or Javascript, to be honest. I'll see if I can give it a shot
this weekend. Curious to really know. You could certainly use more, but the
same could be said of Haskel, as well.

But I'm not necessarily trying to make a claim that it can't be done in
Haskel. More that I have not seen it done. You can claim that the final polish
of the original is irrelevant, but I find that claim hard to believe.
Especially since I have yet to see an example that has said polish.

Sadly, I don't know how you would really go about proving that one way or the
other.

~~~
gu
I never claimed that the polish of the UI in the original was irrelevant.

~~~
taeric
So, I realize the odds are rather low you will see this. I took a stab at
implementing 2048 in javascript. Depending on what you mean by "many more
lines," I'm not sure I agree still. For a hardcoded 4x4 grid, I'm only at 100
lines of code in javascript. (For arbitrary rectangular boards, I'm at 130.)

I'm not going to claim that the code alone is as "pretty" as the Haskel
version, but I see no reason it should have exploded to be much larger. I may
have to take a look at how other folks are doing this.

(Code is currently at
[https://github.com/taeric/2048-Explorations](https://github.com/taeric/2048-Explorations).
Will try to prettify and publish the org file that accompanies it.)

------
cwhy
I haven't used haskell before and did not fully understand the code, but since
python support those map filter stuff, can we write haskell-like python as
well?

