Nick
Nick

Reputation: 97

How to pass system time to a variable in Haskell

I'm trying to get the current POSIX time from the system clock using Haskell but get the following error:

    • No instance for (Show (IO POSIXTime))
        arising from the sixth field of ‘Block’ (type ‘IO POSIXTime’)
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (Show Block)
   |
16 |                    , nonce          :: Maybe Integer } deriving Show
   |                                                                 ^^^^

Here is the code for context:

import Data.Time
import Data.Time.Clock.POSIX

data Transaction = Transaction { from  :: String
                               , to    :: String
                               , value :: Float } deriving Show

data Block = Block { version        :: Int
                   , index          :: Int
                   , transactions   :: [Transaction]
                   , prevBlockHash  :: String
                   , merkleRootHash :: String
                   , time           :: IO POSIXTime
                   , nonce          :: Maybe Integer } deriving Show

genesis :: Block
genesis = Block version index transactions prevBlockHash merkleRootHash time Nothing
    where version = 1
          index = 0
          transactions = []
          prevBlockHash = "000000000000000000000000000000000"
          merkleRootHash = ""
          time = getPOSIXTime
          nonce = Nothing

I've referenced a number of solutions to similar questions on stack overflow but haven't been able to get past the error for a few days.

Upvotes: 2

Views: 192

Answers (2)

pedrofurla
pedrofurla

Reputation: 12783

Let's begin by using a illustration of the of the type you have

data Block = Block { 
                     version        :: Int
                   -- removed a few field for illustrative purposes 
                   , time           :: IO POSIXTime
                   , nonce          :: Maybe Integer } deriving Show

As was commented IO POSIXTime is not really a POSIXTime, but a representation of the action of obtaining a POSIXTime. And let's change the type to an actual POSIXTime.

data Block = Block { 
                     version        :: Int
                   , time           :: POSIXTime
                   , nonce          :: Maybe Integer } deriving Show

Now we can adapt genesis to deal with our new Block: import Data.Time import Data.Time.Clock.POSIX

data Block = Block { 
                     version        :: Int
                   , time           :: POSIXTime
                   , nonce          :: Maybe Integer } deriving Show


genesis :: IO Block
genesis = 
  do
     time <- getPOSIXTime
     let version = 1
     let nonce = Nothing
     return (Block version time nonce)

blockTimeAsUTCString :: Block -> String -- No need for IO
blockTimeAsUTCString (Block {time = p}) = show $ posixSecondsToUTCTime p

main :: IO ()
main = ioBlock >>= print >>
       ioBlock >>= putStrLn . blockTimeAsUTCString
       where ioBlock = genesis
            

Notice how we moved IO from inside Block to outside and how it plays well with the fact that an Haskell executable is always an IO () and blockTimeAsUTCString shows how the record can be processed without relying on IO.

Upvotes: 8

Popara
Popara

Reputation: 4640

If you are really stuck with such type signature for Block I would go on implementing Show (IO POSIXTIme) :

instance Show (IO POSIXTime) where 
  show _ = "Time shower"  

you might go and make more general Show (IO a) instance

instance Show (IO a) where 
  show _ = "I can't Show what is inside this IO monad" 

Ultimately, you can write your own Show instance of the Block instead of deriving it:

instance Show Block where 
  show block = "Block " <> show (version block) <> " .... " 

otherwise change Block type signature to be just POSIXTime not wrapped in IO?

Upvotes: -4

Related Questions