How can I bind a template to specific elements in a data structure?

Consider a simple data structure below (I am learning how to use attoparsec). Instead of just displaying the show automatically, I created an instance for it. However, this instance will break immediately (return a wrong answer) if the order of the items in the DateDefinition is changed (for example, I put dayOfMonth before the monthOFYear). There must be a way to associate every record in the template with every record in the data structure so that it doesn't depend on changes in element order. But how? I tried using the actual names, but unsurprisingly it didn't work and just resulted in a warning about the existing binding being shadowed.

data DateDefinition = DateDefinition
            {
                 monthOfYear :: Months,
                 dayOfMonth :: Int,
                 hourOfDay :: Int,
                 minuteOfHour :: Int,
                 secondOfMinute :: Int
            }

instance Show DateDefinition where
   show (DateDefinition m d _ _ _) = show m  ++  " " ++ show d

      

+3


source to share


3 answers


You can use Record Puns to briefly match records with an extension NamedFieldPuns

:

instance Show DateDefinition where
    show (DateDefinition {monthOfYear, dayOfMonth}) =
        show monthOfYear ++ " " ++ show dayOfMonth

      

You can also match patterns in a similar way without extension:

instance Show DateDefinition where
    show (DateDefinition {monthOfYear = month, dayOfMonth = day}) =
        show month ++ " " ++ show day

      

If you don't really want to match patterns, you can do it in a dirtier way using functions that retrieve values:



instance Show DateDefinition where
    show date =
        show (monthOfYear date) ++ " " ++ show (dayOfMonth date)

      

It is definitely more than others.


It is also possible to rename your datatype to Date

and change the functions to keep the code shorter, because the names you are using now are not that big:

data Date = Date 
           { month     :: Months
           , day       :: Int
           , hour      :: Int
           , minute    :: Int
           , second    :: Int
           }

      

+4


source


You can use field accessors directly; this is the easiest way:

instance Show DateDefinition where
    show dd = show (monthOfYear dd) ++ " " ++ show (dayOfMonth dd)

      

Alternatively, you can use the extension RecordWildCards

:



instance Show DateDefinition where
    show DateDefinition{..} = show monthOfYear ++ " " ++ show dayOfMonth

      

This is fine if you never plan to get rid of these fields. If you want to change the internal structure without breaking anything, then the first implementation ( as pointed out by the framework ) is probably the safest. You can always turn these fields into regular functions without knowing if you are not exporting the constructor.

+5


source


You can match patterns using field names:

instance Show DateDefinition where
   show (DateDefinition { monthOfYear = m, dayOfMonth = d }) 
     = show m ++  " " ++ show d

      

Alternatively, if you are worried about future validation, to get rid of these fields entirely, you can use ViewPatterns

:

instance Show DateDefinition where
   show (monthOfYear -> m)@(dayOfMonth -> d) = show m ++  " " ++ show d

      

(no, can't do it with @

).

+3


source







All Articles