Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The Rust and Haskell versions are complicated to understand for such a simple task.


IMHO, the Haskell version using mapAccumL[1] is actually quite a nice demonstration of the power of having lots of computational patterns available immediately from the standard library.

The biggest problem in Haskell that a language like JS doesn’t have is the types: you’re converting both the outer type for sections and the inner type for lessons into something else, so in any language with explicit static types you’re going to have the overhead of specifying those, or you have to hack around it a bit as we see here.

However, if you ignore the type boilerplate and the definition of the input data, the substance of the algorithm here would be around the same length as the imperative JS for-of version, particularly with less verbose names for the local variables as idiomatic Haskell tends to use.

With all four types explicitly defined and using another map-accumulate instead of the zipWith so updating the lessons with their positions was done similarly to updating the sections, I think it could be quite readable as well, even with Haskell’s limited support for record types.

[Edit: Having actually tried this now, even with DuplicateRecordFields, my Haskell code still seems unnecessarily verbose because of the record manipulations, but I still quite like the use of mapAccumL for making the pattern of computation explicit.]

[1] https://github.com/josevalim/nested-data-structure-traversal...


I've always liked `mapAccumL`, and nowadays, it amuses that it is implemented by mapping with a state monad (and thus it works over arbitrary Traversables).

With that precedent, I'm happy to implement the given problem using State and traverse, so it's structured much the same as the obvious imperative version:

    incr :: (MonadState s m, Num s) => m s
    incr = get <* modify (+1)

    annotate :: [Section x] -> [Section Int]
    annotate ss = flip evalState 1 . forM (zip [1..] ss) $ \(i,Section title reset lessons _) -> do

      when reset (put 1)

      lessons <- (traverse . traverse) (const incr) lessons

      pure $ Section title reset lessons i


That seems to be a choice of the author, a new approach has been uploaded that uses the exact same logic as the python approach and is similarly as succinct.

https://github.com/josevalim/nested-data-structure-traversal...


The map-mutable variant in Rust doesn't seem all too complicated to me; but I have the advantage of being used to the iterator+closure syntax which admittedly can look unnecessarily confusing sometimes.

I personally often avoid long chained iterator operations, especially if they involve `for_each()`. Usually they're barely shorter than a for loop but seem to be harder to figure out for the type & ownership checker, leading to annoying issues with less helpful error messages. And just like with pipes in Bash I often run into situations where I wish I had multiple "channels" through which I can pass data, and a loop doesn't have any limitation like that.

Luckily there are multiple neat ways to solve problems in the language.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: