all 80 comments

sorted by: hot top controversial new old
[–] 100 points 1 year ago (10 children)

You gotta admit though, Haskell is crazy good for parsing and marshaling data

  • source
  • hideshow 10 child comments
  • [–] 42 points 1 year ago (8 children)

    Yes. I'm divided into "hum... 100 lines is larger than I expected" and "what did he mean 'from scratch'? did he write the parser combinators? if so, 100 lines is crazy small!"

    But I'm settling in believing 80 of those lines are verbose type declarations.

  • source
  • parent
  • hideshow 8 child comments
  • [–] 15 points 1 year ago* (last edited 1 year ago) (2 children)

    I decided to write it myself for fun. I decided that "From Scratch" means:

    • No parser libraries (parsec/happy/etc)
    • No using read from Prelude
    • No hacky meta-parsing

    Here is what I came up with (using my favourite parsing method: parser combinators):

    import Control.Monad ((>=>), replicateM)
    import Control.Applicative (Alternative (..), asum, optional)
    import Data.Maybe (fromMaybe)
    import Data.Functor (($>))
    import Data.List (singleton)
    import Data.Map (Map, fromList)
    import Data.Bifunctor (first, second)
    import Data.Char (toLower, chr)
    
    newtype Parser i o = Parser { parse :: i -> Maybe (i, o) } deriving (Functor)
    
    instance Applicative (Parser i) where
      pure a = Parser $ \i -> Just (i, a)
      a <*> b = Parser $ parse a >=> \(i, f) -> second f <$> parse b i
    instance Alternative (Parser i) where
      empty = Parser $ const Nothing
      a <|> b = Parser $ \i -> parse a i <|> parse b i
    instance Monad (Parser i) where
      a >>= f = Parser $ parse a >=> \(i, b) -> parse (f b) i
    instance Semigroup o => Semigroup (Parser i o) where
      a <> b = (<>) <$> a <*> b
    instance Monoid o => Monoid (Parser i o) where
      mempty = pure mempty
    
    type SParser = Parser String
    
    charIf :: (a -> Bool) -> Parser [a] a
    charIf cond = Parser $ \i -> case i of
      (x:xs) | cond x -> Just (xs, x)
      _ -> Nothing
    
    char :: Eq a => a -> Parser [a] a
    char c = charIf (== c)
    
    one :: Parser i a -> Parser i [a]
    one = fmap singleton
    
    str :: Eq a => [a] -> Parser [a] [a]
    str = mapM char
    
    sepBy :: Parser i a -> Parser i b -> Parser i [a]
    sepBy a b = (one a <> many (b *> a)) <|> mempty
    
    data Decimal = Decimal { mantissa :: Integer, exponent :: Int } deriving Show
    
    data JSON = Object (Map String JSON) | Array [JSON] | Bool Bool | Number Decimal | String String | Null deriving Show
    
    whitespace :: SParser String
    whitespace = many $ asum $ map char [' ', '\t', '\r', '\n']
    
    digit :: Int -> SParser Int
    digit base = asum $ take base [asum [char c, char (toLower c)] $> n | (c, n) <- zip (['0'..'9'] <> ['A'..'Z']) [0..]]
    
    collectDigits :: Int -> [Int] -> Integer
    collectDigits base = foldl (\acc x -> acc * fromIntegral base + fromIntegral x) 0
    
    unsignedInteger :: SParser Integer
    unsignedInteger = collectDigits 10 <$> some (digit 10)
    
    integer :: SParser Integer
    integer = asum [char '-' $> (-1), char '+' $> 1, str "" $> 1] >>= \sign -> (sign *) <$> unsignedInteger
    
    -- This is the ceil of the log10 and also very inefficient
    log10 :: Integer -> Int
    log10 n
      | n < 1 = 0
      | otherwise = 1 + log10 (n `div` 10)
    
    jsonNumber :: SParser Decimal
    jsonNumber = do
      whole <- integer
      fraction <- fromMaybe 0 <$> optional (str "." *> unsignedInteger)
      e <- fromIntegral . fromMaybe 0 <$> optional ((str "E" <|> str "e") *> integer)
      pure $ Decimal (whole * 10^log10 fraction + signum whole * fraction) (e - log10 fraction)
    
    escapeChar :: SParser Char
    escapeChar = char '\\'
      *> asum [
        str "'" $> '\'',
        str "\"" $> '"',
        str "\\" $> '\\',
        str "n" $> '\n',
        str "r" $> '\r',
        str "t" $> '\t',
        str "b" $> '\b',
        str "f" $> '\f',
        str "u" *> (chr . fromIntegral . collectDigits 16 <$> replicateM 4 (digit 16))
      ]
    
    jsonString :: SParser String
    jsonString =
      char '"'
      *> many (asum [charIf (\c -> c /= '"' && c /= '\\'), escapeChar])
      <* char '"'
    
    jsonObjectPair :: SParser (String, JSON)
    jsonObjectPair = (,) <$> (whitespace *> jsonString <* whitespace <* char ':') <*> json
    
    json :: SParser JSON
    json =
      whitespace *>
        asum [
          Object <$> fromList <$> (char '{' *> jsonObjectPair `sepBy` char ',' <* char '}'),
          Array <$> (char '[' *> json `sepBy` char ',' <* char ']'),
          Bool <$> asum [str "true" $> True, str "false" $> False],
          Number <$> jsonNumber,
          String <$> jsonString,
          Null <$ str "null"
        ]
        <* whitespace
    
    main :: IO ()
    main = interact $ show . parse json
    
    

    This parses numbers as my own weird Decimal type, in order to preserve all information (converting to Double is lossy). I didn't bother implementing any methods on the Decimal, because there are other libraries that do that and we're just writing a parser.

    It's also slow as hell but hey, that's naive implementations for you!

    It ended up being 113 lines. I think I could reduce it a bit more if I was willing to sacrifice readability and/or just inline things instead of implementing stdlib typeclasses.

  • source
  • parent
  • hideshow 2 child comments
  • [+] 6 points 1 year ago (2 children)
  • load more comments (-1 replies)
  • [–] 4 points 1 year ago

    Just looking at the image, yeah he's a little parser combinator library entirely from scratch.

    Not sure what you mean by verbose type declarations. It looks to be 2 type declarations in a few lines of code (a newtype for the parser and a sum type to represent the different types of JSON values). It's really not much at all.

  • source
  • parent
  • [–] 78 points 1 year ago (1 child)

    Who needs a girl when you have monads to keep you warm?

  • source
  • hideshow 1 child comment
  • [–] 55 points 1 year ago* (last edited 1 year ago) (1 child)

    No mom, I'm gonna BE a girl for Christmas. puts on programming socks

  • source
  • hideshow 1 child comment
  • [+] 33 points 1 year ago (2 children)
  • [–] 26 points 1 year ago (6 children)
  • [–] 7 points 1 year ago (4 children)

    not sure if it's sarcastic: point of view

  • source
  • parent
  • hideshow 4 child comments
  • [–] 6 points 1 year ago (2 children)

    I did consider taking a screenshot before the edit from POW to POV, but eh.

  • source
  • parent
  • hideshow 2 child comments
  • load more comments (-1 replies)
    [–] 23 points 1 year ago (2 children)

    NOTE: no proper error reporting

    Add those few lines, will ya?

  • source
  • hideshow 2 child comments
  • [–] 81 points 1 year ago (8 children)

    Jokes on her, I've transitioned since last Christmas.

  • source
  • hideshow 8 child comments
  • [–] 78 points 1 year ago (2 children)

    You can still bring a girl though

  • source
  • parent
  • hideshow 2 child comments
  • [–] 60 points 1 year ago (4 children)

    I am the girl! Hmm, but maybe I'll bring another one too? 🤔

  • source
  • parent
  • hideshow 4 child comments
  • [–] 14 points 1 year ago (3 children)
  • [–] 4 points 1 year ago (2 children)

    it's yuletide! everyone (except that person. they know what they did) is welcome and celebrated!

  • source
  • parent
  • hideshow 2 child comments
  • [–] 36 points 1 year ago (2 children)

    You just need to find a girl that also likes Tsoding! Then, you can ask her "Hey, do you have plans for Christmas? I'd love it if we could do AoC (Advent of Code) in a language we both hate!"

  • source
  • hideshow 2 child comments
  • [–] 35 points 1 year ago (19 children)

    There are far more male programmers... As a programmer, be gay or stay alone... Choose!

  • source
  • hideshow 19 child comments
  • [–] 32 points 1 year ago (7 children)

    Oh that explains why my wife is gay

  • source
  • parent
  • hideshow 7 child comments
  • [–] 4 points 1 year ago (3 children)

    She sleeps with men, that's pretty gay

  • source
  • parent
  • hideshow 3 child comments
  • [–] 21 points 1 year ago (6 children)

    Can programmers only be with other programmers or am I missing something?

  • source
  • parent
  • hideshow 6 child comments
  • [–] 20 points 1 year ago (3 children)

    "JSON parser 100% from scratch in Haskell in 110 lines" doesn't get you horny? I guess some people are just wired differently.

  • source
  • parent
  • hideshow 3 child comments
  • [–] 4 points 1 year ago (2 children)

    I’m a programmer myself but my wife isn’t a programmer, that was my motivation for questioning.

  • source
  • parent
  • hideshow 2 child comments
  • [–] 5 points 1 year ago

    Don't worry. I mean nothing but humour by it. I myself am married to a physicist who takes absolutely no interest whatsoever in programming, but can talk happily for ages about something weird they found.

  • source
  • parent
  • [–] [S] 17 points 1 year ago (1 child)

    There are those who transition, so a significant chunk of that male programmer population is "male" as in quotation marks, only that some transition earlier than others. Does not guarantee that you can get the transgender autistic puppygirl (or other variations) of your dreams, since many of them are lesbians.

    But also feel free to look outside your field for a partner. It's okay to date an artist as a programmer.

  • source
  • parent
  • hideshow 1 child comment
  • [–] 4 points 1 year ago

    It's odd in the Australian public service, with COBOL programmers. They've been in the job long enough that they started when the public service was the only employer who would employ women as programmers. I'm on the systems analyst side of the fence, the programmers I have worked with include a bit more than 60% women

    I think all the programmers I know are married or gay or not interested. I think the gay ones are mostly married too.

  • source
  • parent
  • [–] 16 points 1 year ago (3 children)
  • [–] 15 points 1 year ago

    I wouldn't trust a guy letting their battery go that low either

  • source
  • [–] 13 points 1 year ago (1 child)

    I'm a girl. I'm not interested in Haskell, that's too frigging endofunctiorific. Erlang! That's what all the cool guys are doing.

  • source
  • hideshow 1 child comment
  • [–] 9 points 1 year ago

    Hello everyone, and welcome to yet another recreational programming session with who?

  • source
  • [–] 9 points 1 year ago*

    He won't be done debugging her by then. She'll be ready for beta testing next year.

  • source
  • [–] 11 points 1 year ago (4 children)

    A JSON parser in Haskell, what a day to have eyes

  • source
  • hideshow 4 child comments
  • [–] 1 point 1 year ago (3 children)

    ?

    Haskell's incredibly good for writing parsers.

  • source
  • parent
  • hideshow 3 child comments
  • [–] 1 point 1 year ago (2 children)

    But oh boy is it difficult. We started with Haskell in the first semester CS and it was a pain. Kudos to anyone seriously developing in Haskell.

  • source
  • parent
  • hideshow 2 child comments
  • [–] 3 points 1 year ago (1 child)

    Eh, it's just different. Other languages are hard in other ways. Haskell's at least have very good reason behind them.

    I write Haskell professionally and and am teaching to people without any experience, and it's really no different than anything else. Though I will say that my experience is that university professors are often pretty clueless about the language and don't teach it well.

  • source
  • parent
  • hideshow 1 child comment
  • [–] 3 points 1 year ago

    I think it's the paradigm change. Most people including myself learnt some kind of procedural language in school, shifting towards functional thinking is just very difficult. But of course that's a skill a computer scientist must have and one of the reasons I didn't graduate.

  • source
  • parent
  • [–] 4 points 1 year ago

    Based Tsoding

  • source
  • [–] 4 points 1 year ago

    Well, JSON is an easy format to parse. The spec can fit onto one page.

  • source
  • [–] 5 points 1 year ago (1 child)

    If you are writing a parser in haskell just use Happy and get it over with

  • source
  • hideshow 1 child comment