Chris Stryczynski
Chris Stryczynski

Reputation: 34021

How can I get a Binary instance of the thyme package's UTCTime data type?

I'd like to be able to serialize CommandRecord into binary to be able to save it to a file. However my naive approach of just doing: instance Binary CommandRecord does not work due the below error*.

What approach can I use to achieve the above? Right now my thinking would be to work around this issue by, abandoning the idea of using the thyme library and it's UTCTime and instead use time library and it's UTCTime (which has a Binary instance already defined).


I have the follow data type:

data CommandRecord = CommandRecord {
    command :: Text
  , timedate :: UTCTime
  , path :: Text
  } deriving Generic

I have the following imports:

import Data.Thyme.Clock
import Data.Binary
import Data.Binary.Orphans

I'm using the following packages:

Error*:

/home/chris/Projects/Haskell/MoscoviumOrange/src/Main.hs:28:10-
29: error:
    • No instance for (Binary UTCTime)
        arising from a use of ‘binary-0.8.5.1:Data.Binary.Class
.$dmput’
      There are instances for similar types:
        instance Binary
                   time-1.8.0.2:Data.Time.Clock.Internal.UTCTim
e.UTCTime
          -- Defined in ‘Data.Binary.Orphans’
    • In the expression:
        binary-0.8.5.1:Data.Binary.Class.$dmput @CommandRecord
      In an equation for ‘put’:
          put = binary-0.8.5.1:Data.Binary.Class.$dmput 
@CommandRecord
      In the instance declaration for ‘Binary CommandRecord’
   |
28 | instance Binary CommandRecord
   |          ^^^^^^^^^^^^^^^^^^^^
/home/chris/Projects/Haskell/MoscoviumOrange/src/Main.hs:28:10-
29: error:
    • No instance for (Binary UTCTime)
        arising from a use of ‘binary-0.8.5.1:Data.Binary.Class
.$dmget’
      There are instances for similar types:
        instance Binary
                   time-1.8.0.2:Data.Time.Clock.Internal.UTCTim
e.UTCTime
          -- Defined in ‘Data.Binary.Orphans’
    • In the expression:
        binary-0.8.5.1:Data.Binary.Class.$dmget @CommandRecord
      In an equation for ‘get’:
          get = binary-0.8.5.1:Data.Binary.Class.$dmget 
@CommandRecord
      In the instance declaration for ‘Binary CommandRecord’
   |
28 | instance Binary CommandRecord
   |          ^^^^^^^^^^^^^^^^^^^^

Upvotes: 0

Views: 139

Answers (2)

Chris Stryczynski
Chris Stryczynski

Reputation: 34021

Following with the 1. point from @leftaroundabout I ended up with the following module:

{-# OPTIONS -Wno-orphans #-}
module ThymeBinaryInstances where

import Data.Binary
import Data.Thyme.Internal.Micro
import Data.Thyme.Clock

instance Binary UTCTime
instance Binary NominalDiffTime
instance Binary Micro

Upvotes: 0

leftaroundabout
leftaroundabout

Reputation: 120741

Ah yes, the trivial-but-missing instance problem. What I would do:

  1. Add instance Binary Thyme as an orphan instance to yourmodule. Confirm that it works.
  2. Wrap the instance in an #if !MIN_VERSION_thyme(0,3,6) switch. The reason is, if a new version of thyme appears that already includes the instance too, you don't want the duplicate instance to break your build.
    Also make sure you have the dependency bound thyme<=0.3.5 in your .cabal file, so a new version without the instance won't be tried. (This may seem to make the switch on the instance useless. The point is, you can edit the dependency bounds on Hackage after the fact, and should do so when a thyme version with the instance appears.)
  3. File a pull request to the thyme maintainers that adds the instance. This should be uncontroversial, since thyme indirectly depends on Binary anyway.

This is only advisable if what you're writing is an executable or small specialists library, though. If it's a library that lots of other projects might use, there's a risk of conflicting with somebody else's orphan instance. In that case, you should instead use a workaround until thyme adds the instance:

data CommandRecord = CommandRecord {
    command :: Text
  , timedate :: UTCTime'
  , path :: Text
  } deriving Generic

newtype UTCTime' = UTCTime' {stdUTCThyme :: UTCTime}
instance Binary UTCTime' where
  put = gput . from . stdUTCThyme
  get = UTCTime' . to <$> gget

Upvotes: 1

Related Questions