debugMinor
Interpreting database values as times and integers
Viewed 0 times
andinterpretingdatabasevaluestimesintegers
Problem
I am working through Chapter 10 of Haskell Programming from First Principles.
I've gotten the code to work, but I am uneasy about the fact that a few of my functions - namely,
```
import Data.Time
data DatabaseItem = DbString String | DbNumber Integer | DbDate UTCTime deriving (Eq, Ord, Show)
theDatabase :: [DatabaseItem]
theDatabase = [DbDate (UTCTime (fromGregorian 1911 5 1) (secondsToDiffTime 34123)), DbNumber 9001, DbString "Hello, world!", DbDate (UTCTime (fromGregorian 1921 5 1) (secondsToDiffTime 34123))]
filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate [] = []
filterDbDate dataBase = [extractUTCTime item | item Bool
isDbDate (DbDate _) = True
isDbDate _ = False
extractUTCTime :: DatabaseItem -> UTCTime
extractUTCTime (DbDate utcData) = utcData
filterDbNumber :: [DatabaseItem] -> [Integer]
filterDbNumber [] = []
filterDbNumber dataBase = [extractInteger item | item Integer
extractInteger (DbNumber int) = int
isDbNumber :: DatabaseItem -> Bool
isDbNumber (DbNumber _) = True
isDbNumber _ = False
mostRecent :: [DatabaseItem] -> UTCTime
mostRecent dataBase = foldr max y ys
where dates = filterDbDate dataBase
y = head dates
ys = tail dates
sumDb :: [DatabaseItem] -> Integer
sumDb dataBase = foldr (+) 0 dbNumbers
where dbNumbers = filterDbNumber dataBase
avgDb :: [DatabaseItem] -> Double
avgDb dataBase = dividend / divisor
where dividend = fromInteg
I've gotten the code to work, but I am uneasy about the fact that a few of my functions - namely,
extractUTCTime and extractInteger - are not total functions. There is valid input for which those functions are bottom. I've added the code below, but I was wondering what strategies are available for making extractUTCTime and extractInteger safe. (One strategy that I do know of is changing the type signature of these functions so that they return a value of type Maybe: extractUTCTime :: DatabaseItem -> Maybe UTCTime. I was wondering if there was anything else that can be done).```
import Data.Time
data DatabaseItem = DbString String | DbNumber Integer | DbDate UTCTime deriving (Eq, Ord, Show)
theDatabase :: [DatabaseItem]
theDatabase = [DbDate (UTCTime (fromGregorian 1911 5 1) (secondsToDiffTime 34123)), DbNumber 9001, DbString "Hello, world!", DbDate (UTCTime (fromGregorian 1921 5 1) (secondsToDiffTime 34123))]
filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate [] = []
filterDbDate dataBase = [extractUTCTime item | item Bool
isDbDate (DbDate _) = True
isDbDate _ = False
extractUTCTime :: DatabaseItem -> UTCTime
extractUTCTime (DbDate utcData) = utcData
filterDbNumber :: [DatabaseItem] -> [Integer]
filterDbNumber [] = []
filterDbNumber dataBase = [extractInteger item | item Integer
extractInteger (DbNumber int) = int
isDbNumber :: DatabaseItem -> Bool
isDbNumber (DbNumber _) = True
isDbNumber _ = False
mostRecent :: [DatabaseItem] -> UTCTime
mostRecent dataBase = foldr max y ys
where dates = filterDbDate dataBase
y = head dates
ys = tail dates
sumDb :: [DatabaseItem] -> Integer
sumDb dataBase = foldr (+) 0 dbNumbers
where dbNumbers = filterDbNumber dataBase
avgDb :: [DatabaseItem] -> Double
avgDb dataBase = dividend / divisor
where dividend = fromInteg
Solution
I was wondering if there was anything else that can be done
Well, morally, no. You can either return a nullable value (
The good news is, if you hit on the right set of higher-order functions you don't need to.
Taking as a whole this set of functions related to dates—
We'll begin by throwing out
Now pulling from
We can use that definition in
Well, morally, no. You can either return a nullable value (
Maybe UTCTime) or lie (read "1970-01-01 00:00:00 UTC" :: UTCTime) but you can't turn a DbNumber 1 :: DatabaseItem into a UTCTime both unconditionally and truthfully.The good news is, if you hit on the right set of higher-order functions you don't need to.
Maybe values are very easy to work with when you know the extra tools available in Data.Maybe.Taking as a whole this set of functions related to dates—
filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate [] = []
filterDbDate dataBase = [extractUTCTime item | item Bool
isDbDate (DbDate _) = True
isDbDate _ = False
extractUTCTime :: DatabaseItem -> UTCTime
extractUTCTime (DbDate utcData) = utcData
mostRecent :: [DatabaseItem] -> UTCTime
mostRecent dataBase = foldr max y ys
where dates = filterDbDate dataBase
y = head dates
ys = tail datesWe'll begin by throwing out
isDbDate and extractUTCTime and writing the DatabaseItem -> Maybe UTCTime function you avoided.dbDate :: DatabaseItem -> Maybe UTCTime
dbDate (DbDate utc) = Just utc
dbDate _ = NothingNow pulling from
Data.Maybe, we'll replace filterDbDate with an equivalent code fragment that utilizes mapMaybe.-- filterDbDate :: [DatabaseItem] -> [UTCTime]
-- filterDbDate db =
mapMaybe dbDate dbWe can use that definition in
mostRecent, a function from the standard Prelude, and point free style to achieve a one liner that more than just being clever, emphasizes the power of abstraction in producing concise code that will be instantly comprehensible to other Haskell programmers.mostRecent :: [DatabaseItem] -> UTCTime
mostRecent = maximum . mapMaybe dbDatemostRecent still commits the sin of using a partial function though, maximum throws an exception (much like your use of head and tail did before) when the filtered list is empty. Until such time as partial functions are excised from base (and O, do we all lust for and fear such a tumultuous day) you should familiarize yourself with the safe package, which has no dependencies besides base, and provides many alternatives to the partial functions we all know and love.import Safe.Foldable (maximumMay)
mostRecent :: [DatabaseItem] -> Maybe UTCTime
mostRecent = maximumMay . mapMaybe dbDateCode Snippets
filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate [] = []
filterDbDate dataBase = [extractUTCTime item | item <- dataBase, isDbDate item]
isDbDate :: DatabaseItem -> Bool
isDbDate (DbDate _) = True
isDbDate _ = False
extractUTCTime :: DatabaseItem -> UTCTime
extractUTCTime (DbDate utcData) = utcData
mostRecent :: [DatabaseItem] -> UTCTime
mostRecent dataBase = foldr max y ys
where dates = filterDbDate dataBase
y = head dates
ys = tail datesdbDate :: DatabaseItem -> Maybe UTCTime
dbDate (DbDate utc) = Just utc
dbDate _ = Nothing-- filterDbDate :: [DatabaseItem] -> [UTCTime]
-- filterDbDate db =
mapMaybe dbDate dbmostRecent :: [DatabaseItem] -> UTCTime
mostRecent = maximum . mapMaybe dbDateimport Safe.Foldable (maximumMay)
mostRecent :: [DatabaseItem] -> Maybe UTCTime
mostRecent = maximumMay . mapMaybe dbDateContext
StackExchange Code Review Q#146305, answer score: 3
Revisions (0)
No revisions yet.