1. 29
  1. 7

    The announcement has an interesting argument, not specific to Haskell, for conservatism in language design:

    We spent a lot of time discussing more expressive options. […] But we chose a more conservative path for now. By making naked record selectors illegal (except in parens) we leave those possibilities open for the future, as we get more experience. (This is one reason for not adopting […] naked record selectors without parens.)

    1. 5

      This is the most thoroughly bikeshedded proposal I have ever seen! I like the chosen syntax, though, and I’m really glad this feature is making it into Haskell.

      Anyone have a guess about how long it will be before a released version of GHC supports the new syntax?

      1. 2

        OMG, FINALLY, YES! Yes, yes, yes! Woohoo!

        1. 1

          And Haskell gets 1 step more complex for no gain, yet again. It’s rare to see Haskell code without copious numbers of language extensions these days. It’s becoming the new Perl, a write-only language.

          I think it’s time we went back to writing simple Haskell with few or no language extensions. It makes life a LOT easier.

          1. 1

            Eventually we would expect many extensions, likely this one included, merged into a new language standard. So if the concern is about there being too many language extensions, knowing a new standard is inevitable may assuage.

            As for this specific extension, records in Haskell are a mess. Namespacing issues and access/modification of nested fields are the most critical. This proposal provides a syntax which can be used to select fields—having that resolved enables other improvements. I can understand preferring a different syntax, but I’m surprised anyone is opposed to improving records.

            1. 1

              Records in Haskell are absolutely fine as they are in the base language. The namespacing issue is not a real issue in practice. Access to nested fields is not remotely difficult, it’s just the composition of functions. You cannot modify fields in Haskell, so I’m not sure why you’d say that ‘modification’ is a critical issue.

              1. 1

                I can agree to disagree that namespacing is an issue.

                By “modification” I mean a convenient way to generate a new, updated record with a different value for the selected field. If you’re familiar with the lens package then think of the set function.

                1. 1

                  Almost all the use I see of lens just tries to duplicate imperative programming in Haskell, usually defeating the whole point of writing Haskell in the first place. It’s for people that haven’t learnt how to write functional code.

                  1. 2

                    I would argue the contrived example below actually demonstrates that lens enables code that is more functional-y. See how we can use over in operator form (%~) to update the value of a field with a function? This example isn’t complex enough that I’d necessarily reach for lenses, but if I was going to create an entire library for creating and manipulating shapes I would.

                    You might want to look more closely at optics. While I can understand the complexity-based arguments against them I have never heard someone dismiss them as being a crutch for imperative-minded Haskellers. (Do such people even exist?)

                    {-# LANGUAGE FunctionalDependencies #-}
                    {-# LANGUAGE TemplateHaskell #-}
                    
                    module Contrived where
                    
                    import Control.Lens ((%~), (&))
                    import Control.Lens.TH (makeFields)
                    
                    someFunc :: IO ()
                    someFunc = putStrLn "someFunc"
                    
                    data Point
                      = Point
                          { pointX :: Int,
                            pointY :: Int
                          }
                      deriving (Eq, Ord, Show)
                    
                    $(makeFields ''Point)
                    
                    data Threegon
                      = Threegon
                          { threegonA :: Point,
                            threegonB :: Point,
                            threegonC :: Point
                          }
                      deriving (Eq, Ord, Show)
                    
                    $(makeFields ''Threegon)
                    
                    -- Without lenses
                    shiftX :: Threegon -> Int -> Threegon
                    shiftX tgon val =
                      tgon
                        { threegonA = a {pointX = pointX a + val},
                          threegonB = b {pointX = pointX b + val},
                          threegonC = c {pointX = pointX c + val}
                        }
                      where
                        a = threegonA tgon
                        b = threegonB tgon
                        c = threegonC tgon
                    
                    -- With lenses
                    shiftX' :: Threegon -> Int -> Threegon
                    shiftX' tgon val =
                      tgon 
                        & (a . x) %~ (+ val)
                        & (b . x) %~ (+ val)
                        & (c . x) %~ (+ val)