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

Could you expand on what's the scope of the 'record' intrinsic? Is it used for as many things as Python's `dict`? I sure hope not, otherwise I'm wondering how anyone could have gotten much done in this language before.


It sort of makes me wonder how much of the hype around Haskell is overblown, this seems like pretty basic stuff. Haskell has all kinds of interesting properties but the way it is presented is usually as though it is an end-run around just about every problem in other programming languages. A dose of up-front honesty about potential issues would be handy. Are there any other things that are problematic in Haskell that other programmers from lesser languages usually take for granted?


I've been writing Haskell professionally full-time for five years now and in my opinion no other language even comes close. We've even rewritten all our Javascript in Haskell because maintaining a significant body of Javascript was becoming too much of a burden. So no, I don't think it's overblown at all.

The record problem is not nearly as much of a problem as this discussion makes it seem. Discussions about this problem are invariably going to make it seem like a bigger issue than it is. It's too early to tell, but if this solution really does solve the problem as well as it says, then it will definitely be nice to have, but I don't actually think about this problem very often in day to day work. And lenses have already adequately addressed a significant portion of those pain points.

EDIT: Oh, another point. The fact that this post exists at all is a testimony to the power and flexibility of Haskell. You don't see these "issues" being discussed in other languages because they (other than lisps) don't give you the power to play around with things this close to the language level. So in most other languages the discussion could never happen. And if it did, it would be someone going off and writing a new language.


None of haskell's warts are near bad enough that they should put you off learning the language or using it successfully. Records are a particularly weak point (sort of the simplest thing that could possibly work) which some people care very much about, and others don't care about at all. I think it depends heavily on the sort of code you're writing; when I'm writing libraries I never use records, but others find themselves working with very wide product types with lots of `Int`s or whatever that need to be disambiguated and for them this is a big deal.

I'd say the other biggest warts or deficiencies which are being actively discussed are related to the module system, e.g.: https://ghc.haskell.org/trac/ghc/wiki/Backpack ; I also don't find this to be a huge pain point, but I'm told by people with experience with ML-style modules that we're really missing out with the module system we have.


Records is the thing that any Haskeller will "up-front honestly" tell you is a gripe with the language.


Eh, it's different. Nobody denies that.

In practice I don't significantly care about the "record problem" in Haskell. It is a pain sometimes, but it is not so bad a pain to trade in complexity everywhere like one would have to to use one of these "batteries included" record replacements. As libraries they're alright.


I switched from Python and Go to Haskell over the last year. Haskell takes much longer to compile than Go and cabal hell can really suck. Other than that it's been absolutely fantastic.


I actually did the exact same thing a couple years ago.

> cabal hell can really suck

Agreed, prepare to be happy:

https://www.fpcomplete.com/blog/2015/01/announcing-lts-haske...

> Haskell takes much longer to compile than Go

Also agree, however -O0 in your .cabal file's ghc-options (when -fdevelopment is true) and using -j${NUM_CPU_CORES} can really help.


I think the existence of ghc-mod for incremental compiling and the interpreter repl really don't make compile times a big deal. It's a bigger issue in languages where you actually have to fully compile over and over as a part of your dev loop. This isn't something I ever do in Haskell because of said tools.


What kind of programs do you write?


For fun I'm attracted to various high-level information processing stuff and I do backend web dev at work. Haskell's definitely a Python replacement for me, I'm not writing any OSes in it.


Not the parent, but I made the same transition a couple of years ago. I emailed you something I've wrote in Haskell that might be relevant to you but didn't want to publicize too much yet.


Thank you very much! I'm right now hip deep in Erlang and I very much would like to have a better look at Haskell.


To ease your Haskell experience consider using LTS Haskell or Stackage to avoid Cabal hell:

https://www.fpcomplete.com/blog/2015/01/announcing-lts-haske... http://www.stackage.org/


Then you'll want to see also Cloud Haskell: http://haskell-distributed.github.io/

I'm not sure exactly what its current status is in terms of production-readiness but I believe it is still very active. The GitHub repos seem to be chugging along.


Thanks!


Not to bombard you with too much at once, but one interesting CloudHaskell-related extension that's landing in GHC 7.10 is Static Pointers: safe references to "static" code (constant values, functions, etc.) that can be passed between running instances of a program.

https://ocharles.org.uk/blog/guest-posts/2014-12-23-static-p...


Come along to http://www.reddit.com/r/haskell/ if you like. We're quite friendly in there.


I think you're being downvoted justly, but I think you still deserve an answer to your question (rather than just an onslaught of "hey, don't judge everything by this one thing" type posts).

Haskell's main libraries are based on a simpler model of computation than conventional languages (Scheme/C/JS/Python/...). In this model of computation only two things happen: (a) you define things, (b) expressions are reduced by the language to simpler forms. Furthermore, Haskell's default tendency is to wait when reducing a structure until it's absolutely necessary to reduce it, meaning that you get some nice features (like every list being a generator which can refer to itself in its definition).

Anything which doesn't have an easy model as an expression-reduction (i.e. any side-effect) is more complicated in Haskell. Writing code which is performance-critical (and thus managing when those reductions actually happen) is a bit complicated too.

If only one complicated thing is at play, things are actually pretty tame. So you need multiple of these happening at once before things look a little dicey.

So, in Python you can easily write this:

    state = 0
    for x in list:
        if x % 2 == 0:
            state += x 
        else:
            state = state * (x + 1) - x
        print(state)
In Haskell, the I/O side-effect of `print` is complicated and threading stateful updates through this sort of for-loop is likewise complicated.

One way is to use a high-level idea called "Monad transformers" to keep the complexity down:

    import Control.Monad.State.Strict
    
    flip runStateT 0 $ forM_ list $ do
        state <- get
        if even x then 
            put (x + state) 
        else 
            put (state * (x + 1) - x)
        state <- get
        lift $ putStrLn $ show state
A different way (doing it all with a recursive function) looks like this:

    compute list (return 0)
      where compute [] prog = prog
            compute (x : xs) prog = compute xs $ do
                state <- prog
                let new_state = if even x then state + x else state * (x + 1) - x
                putStrLn $ show $ new_state
                return new_state
This works by having an "accumulating parameter" (prog) which is a computer program; each time we run this recursive function we create another program (do) which reads the last value that prog yielded (state), transforms it into new_state, does the side-effect, and then yields the new_state.

Both of these get the point across but neither one is quite as simple as the Python syntax.

On the flip side, we've been speaking about the above Python code as if we knew exactly what it does, but we actually don't, because we don't know the type of x, so we don't know what x.__mod__ does. For that matter, the list could have two different things with different `__mod__` values and it's possible that the type of `x` could taint the type of `state` so that `print(state)` does something completely unexpected.

In Haskell you can prevent all of this by changing `list` into `(list :: [Integer])`.


It's essentially Haskell's version of a class, except Haskell isn't object-oriented. It holds data. So yes, you could view it as a dict, of sorts. Except each key in this dict/record generates a top-level function.


How did having no namespaces in these things not become a massive headache for everyone? That would be the absolute killer argument for me to immediately switch to a different language if somehow possible.


The main reason it's not a massive headache is simple: Records aren't used THAT widely. Sure, in C++ or Java everything has named fields, because that's the only thing you have. Most Haskell datatypes use ADTs without record syntax, which means fields aren't named.

Personally I only use records for large-ish "configuration" datatypes which and I just don't use that many of those for it to be a real problem.


First, there are namespaces, it's just that they are associated with files instead of particular data types. Secondly, the naming issue only tends to become an issue when you have many types which all have similar internal structure. It turns out that, in Haskell at least, you can avoid needing this often by simply factoring out the shared structure (!). If this is not reasonable then there are boilerplate-y ways to use typeclasses for proper overloading (which also respects namespaces better than this solution does).

Frankly, I have run into only one circumstance where I wanted to throw my computer through a wall due to record inefficiencies... and it only came up while trying to hand translate an the XML specification for ClinicalTrials.gov. It turns out there was a much simpler way to tackle the problem anyway.


So far it's more an inconvience -- in practice it doesn't surface that often, since modules represent the namespaces in Haskell. If you run into naming conflicts very often, chances are your modules are not isolated enough. You don't use records as often as you use classes in an OOP language, for example.

Having said that, a lot of projects seem to have a general Types.hs where they declare all the types they share between modules. This is a great solution for that.


It's not quite true that you have no namespaces. Rather, in most languages a class or struct creates a namespace. In Haskell, it does not. Record accessors are still constrained by module. There are also data types where the fields are not named. And typeclasses can define names that can be used across multiple types.

Between all of these, it's much less painful than you might expect, though there certainly remains some pain.


My thoughts are that it is something of a headache, but the benefits of the language outweigh the pain-points, like this one, in many cases. I'd guess it's seen as one of the trade-offs you need to make for using a pure functional language. (I've yet to see many other languages with a type system as expressive as haskell).


> you cannot have two records share a field name in a single module

(emphasis added.) I'm still a Haskell beginner, so I'm not the best person to comment on how this works out in practice.


Functions and lenses and module namespaces tend to alleviate the problem, not that it isn't annoying now and again. It does make some people go crazy though.


The closest Python analogue is probably named tuples.


It's more like a struct from C.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: