Bradley
Bradley

Reputation: 1281

Expecting a type but actually getting a Maybe a0

I have a function which uses Aeson to get data from a json object within a file. The data and function are both defined below.

data Data = Data { date :: String
                   , temperature :: Int
                    } deriving (Show, Read, Generic)

data Temperatures = Temperatures { temperatures :: [Data]
                                 } deriving (Show, Generic)

mooStuff :: IO Temperatures
mooStuff  = decode <$> getFile
    where
        getFile = B.readFile "Hello.txt"

The file the function is using contains:

Data {date = "2015-02-28T20:16:12+00:00", temperature = 0}
Data {date = "2015-01-01T21:46:55+00:00", temperature = 2}
Data {date = "2015-04-08T21:46:53+00:00", temperature = 3}
Data {date = "2015-04-09T21:46:01+00:00", temperature = 4}
Data {date = "2015-04-10T21:46:40+00:00", temperature = 5}
Data {date = "2015-04-11T21:46:36+00:00", temperature = 6}
Data {date = "2015-04-12T20:36:25+00:00", temperature = 7}

However I'm getting a type error:

test.hs:37:13:
    Couldn't match type ‘Maybe a0’ with ‘Temperatures’
    Expected type: B.ByteString -> Temperatures
      Actual type: B.ByteString -> Maybe a0
    In the first argument of ‘(<$>)’, namely ‘decode’
    In the expression: decode <$> getFile

I've been staring at this for hours now, to no avail. Any ideas about where I'm going wrong would be greatly appreciated!

Upvotes: 1

Views: 247

Answers (1)

pigworker
pigworker

Reputation: 43373

The reason for the error is that decode does not always succeed. Its type is

decode :: Data a => ByteString -> Maybe a

Correspondingly,

(decode <$>) :: Data a => IO ByteString -> IO (Maybe a)
                          -- desired type, IO Temperatures, won't unify

The type error message gives you the abstract Maybe a0 as the actual type because it detects the conflict between Maybe and Temperatures before it gets anywhere near the point of choosing an appropriate instance for the Data a in the type of decode.

Your code needs to account for the possibility that the file delivers its contents but that they don't successfully decode to Temperatures.

Something like

do mt <- decode <$> getFile
   case mt of
     Nothing -> -- handle failure here
     Just t  -> return t

or if you're happy for decode failure to trigger an IO "user error", just

do Just t <- decode <$> getFile
   return t

Any way up, there's a failure mode that your code does not address, and that's what's surfacing as a type error.

Upvotes: 11

Related Questions