Search code examples
haskelltuplesnamednewtype

How to access newtype named tuples fields in haskell


I declare the following newtypes:

newtype Code = Code String deriving (Show)
newtype Name = Name String deriving (Show)
newtype Account = Account (Code, Name) deriving (Show)

So:

*Main Lib> :t Code
Code :: String -> Code
*Main Lib> :t Name
Name :: String -> Name
*Main Lib> :t Account
Account :: (Code, Name) -> Account

and then I create some instances:

cn = Code "1.1.1"
nn = Name "Land And Buildings"
an = Account (cn, nn)

*Main Lib> an
Account (Code "1.1.1",Name "Land And Buildings")

Now I need to access for example just the Code field from the variable an, something like an.Code How can I do that?

Is it better to use Data instead of a newtype? If Haskell lets me create a newtype named tuple, then I guess there should be an easy way to access the elements inside.


Solution

  • Is it better to use data instead of a newtype?

    Um, yes... the whole point of newtype is to give a single type a new name. It's not supposed to be used for building composite types. So, like user2407038 suggested, make it

    data Account = Account
        { accCode :: Code
        , accName :: Name
        } deriving (Show)
    

    and then you can simply use

    *Main Lib> let an = Account (Code "1.1.1") (Name "Land And Buildings")
    *Main Lib> accCode an
    Code "1.1.1"
    

    That said, it's also not difficult to access fields in a tuple buried in a newtype, provided you give the newtype an unwrapper:

    newtype Account = Account {getAccount :: (Code, Name)}
       deriving (Show)
    

    then

    *Main Lib> let an = Account (Code "1.1.1", Name "Land And Buildings")
    *Main Lib> fst $ getAccount an
    Code "1.1.1"
    

    If you want to be fancy, you can also use the “20.2nd century record accessors”, lenses:

    {-# LANGUAGE TemplateHaskell, FunctionalDependencies #-}
    import Lens.Micro
    import Lens.Micro.TH
    
    data Account = Account
        { accountCode :: Code
        , accountName :: Name
        } deriving (Show)
    makeFields ''Account
    

    then

    *Main Lib> let an = Account (Code "1.1.1") (Name "Land And Buildings")
    *Main Lib> an^.code
    Code "1.1.1"