Live data from Hacker News

Show HN: Minimal Lisp in ~70 lines of Haskell

gist.github.com

1–10 of 24 posts

Re: Show HN: Minimal Lisp in ~70 lines of Haskell

#2
I think this might be some of the most beautiful code I've seen in a long while

    value :: Parser Value
    value =
          List  (char8 '(' *> sepBy value (takeWhile1 isSpace_w8)  Number . fst . fromJust . B.readInteger  takeWhile1 isDigit_w8
       Symbol  takeWhile1 (inClass "A-Za-z\\-")
The power of parser combinators representing the elegant lisp syntax.

Re: Show HN: Minimal Lisp in ~70 lines of Haskell

#6
post #3

How long did it take to write?

About 2-3 hours. Lots of breaks though; I started about 16 hours ago and finished about 4 hours ago.

So it's really 6-12 hours. You're working on the code in the background; you probably couldn't just have written it three hours straight. Or more likely, you were sorting out the details within the 12-hour period but you had been toying around with the idea for days if not weeks.

Just guessing and certainly not trying to look down your work, it's beautiful. It's beautiful even if it had taken a week to write. The time to write it is just interesting because it is exactly this that makes programming hard to measure.

How does any explain his productivity to a boss if you get seemingly nothing done for four days and then write a nearly complete solution on the fifth day? How much is one actually working after all, counting all the time that affects the work? Does it count as work if you go biking on a Saturday but you kind of subconsciously think about your work and then you write great stuff on Monday, thanks to that?

One thing is for sure: you're much better off measuring a programmer's efforts by his accomplishments instead of the time to do them. This is backed up by the fact that an entrepreneur programmer gets rewarded much more fairly than a salaried programmer.

Re: Show HN: Minimal Lisp in ~70 lines of Haskell

#8
post #2

I think this might be some of the most beautiful code I've seen in a long while value :: Parser Value value = List (char8 '(' *> sepBy value (takeWhile1 isSpace_w8) Number . fst . fromJust . B.readInteger takeWhile1 isDigit_w8 Symbol takeWhile1 (inClass "A-Za-z\\-") The power of parser combinators representing the elegant lisp syntax.

I'm familiar with parser combinators, but not the Attoparsec or other libraries. Furthermore, I have limited Haskel experience, so I'm struggling to read this. In particular, the use of symbols and overall terseness are hard to get through. I'm also really uncomfortable with the order of operations of all these operators.

Now, I know that Hoogle exists, so I'm able to look these things up, but it's pretty tough to untangle. Particularly when searching for

  *>
I found something that reads "This module describes a structure intermediate between a functor and a monad: it provides pure expressions and sequencing, but no binding. (Technically, a strong lax monoidal functor.)" Which is so full of jargon, my head hurts. And I even know what most of that jargon means!

So as far as I can tell, here's what's going on....

  Foo 
Defines a grammar production using the left argument as a constructor on the matched value of the right argument.

  
Separates productions as alternatives; in a PEG Grammer style. Both the and operators combine functions to result in a big master function that takes input and has the result type that is the big `one of A, B, C` algebraic type that is defined earlier: "Value".

  char8
Matches a literal character (passed as an argument).

  char8 '(' *>
Reads a parenthesis and throws it out of the result set. Similar for the close parenthesis later on.

  takeWhile1 isSpace_w8
Produces a parser that consumes whitespace.

  sepBy value (takeWhile1 isSpace_w8)
Combines to produce a parser which reads a list of values (recursive here) and presumably eliminates the separators from the result.

  takeWhile1 isDigit_w8
Similar story, reads an integer.

  Number . fst . fromJust . B.readInteger
Is a composed constructor that may or may not parse an integer, forcibly assumes it will succeed to parse one (which we know because we're reading digits). That integer parse stops when it hits a non-digit, and the unconsumed characters are returned as the second element of a tuple, so we call fst to take the first element of the tuple, discarding the unread characters (the main parser will consume those).

  takeWhile1 (inClass "A-Za-z\\-")
Similar story again to the whitespace and numbers.

OK, yeah, so.... amazing. Incredibly brilliant little bit of code. But holy hell was that tough to read. And I have no idea if I'd be able to write it in only a few hours. I'm far from certain I'd ever learn to write it as fast or faster than something more verbose.

Please let me know if I've misunderstood something....

Re: Show HN: Minimal Lisp in ~70 lines of Haskell

#9
The content of Fun is actually the type: Context -> Value -> (Context, Value).

If you flip the arguments, you get: Value -> Context -> (Context, Value). The (Context -> (Context, Value)) is actually the State type:

    State s a = s -> (s, a)
I modified your code to use this type. During the process, I encountered some peculiar things (and factored out some things), see comments in code.

https://gist.github.com/950445

Funny that it turns out slightly longer when re-using more code due to syntactic artifacts. Monad comprehensions (recently restored to GHC via an extension) would resolve that.

Re: Show HN: Minimal Lisp in ~70 lines of Haskell

#10
post #2

I think this might be some of the most beautiful code I've seen in a long while value :: Parser Value value = List (char8 '(' *> sepBy value (takeWhile1 isSpace_w8) Number . fst . fromJust . B.readInteger takeWhile1 isDigit_w8 Symbol takeWhile1 (inClass "A-Za-z\\-") The power of parser combinators representing the elegant lisp syntax.

I'm familiar with parser combinators, but not the Attoparsec or other libraries. Furthermore, I have limited Haskel experience, so I'm struggling to read this. In particular, the use of symbols and overall terseness are hard to get through. I'm also really uncomfortable with the order of operations of all these operators. Now, I know that Hoogle exists, so I'm able to look these things up, but it's pretty tough to un…

The syntax elements that you struggled with are explained here: http://learnyouahaskell.com/functors-applicative-functors-an...

They are so incredibly general that you can use them in a lot of places, so the investment to learn their meaning pays off quickly. Learn You A Haskell actually explains them _before_ explaining Monads.

Post reply on HN