Home > database >  How can I access the fields of a data type when using `show` instead of the whole show string?
How can I access the fields of a data type when using `show` instead of the whole show string?

Time:10-29

I come from imperative programming and I am learning Haskell. I have following data types:

newtype Prename = Prename String deriving (Show)
newtype Surname = Surname String deriving (Show)
data Employment = Employed | Unemployed
data Person     = P Prename Surname Employment

jondoe = P (Prename "John") (Surname "Doe") Employed

How can I access the Strings of Prename and Surname without the constructors. Currently I get the following output:

show jondoe
"P (Prename\"John\") (Surname\"Doe\") (Employed)"

but I want it to be

show jondoe
"John Doe"

I am getting very frustrated because it seems like such a simple thing to, how can I achieve this?

CodePudding user response:

show is not for getting pretty output. It is specifically supposed to produce valid Haskell code. Now, "John Doe" certainly is syntactically valid, but it doesn't have the right type.

That said, I personally find it perfectly ok to base a Show instance on custom smart constructors, which would make it more readable. For example, you could have

employee :: Prename -> Surname -> Person
employee pn sn = P pn sn Employed

nonworker :: Prename -> Surname -> Person
nonworker pn sn = P pn sn Unemployed

Furthermore, you may ask yourself whether Prename and Surname really need to be newtypes. If you simply make them

type Prename = String
type Surname = String

then they can be directly printed as... well, strings. Now you could write a Show instance

instance Show Person where
  showsPrec p (P pn sn Employed)
     = showParen (p>9)
        $ ("employee "  )
        . showsPrec 11 pn
        . showsPrec 11 sn
  showsPrec p (P pn sn Unemployed)
     = ...

CodePudding user response:

There are a few ways to do this. One way is to write a function that writes the name to the terminal as follows:

writeName :: Person -> IO ()
writeName (P (Prename p) (Surname s) _) = putStrLn (p    " "    s)
λ> writeName jondoe 
John Doe

Another way is a function that returns the concatenated full name as a string:

getFullname :: Person -> String
getFullname (P (Prename p) (Surname s) _) = p    " "    s
getFullname :: Person -> String
getFullname (P (Prename p) (Surname s) _) = p    " "    s

A third way is to implement an instance of Show for Person as follows:

instance Show Person where
  show (P (Prename p) (Surname s) _) = p    " "    s
λ> show jondoe 
"John Doe"

But to be honest, I'm not sure if implementing a Show instance in this way is a good idea because you loose the Employment information. Having a custom function that explicitly says what it does appears the better way to me.

  • Related