1
votes

Usually I know how to fix ambiguous type variable problems but not this time. Long story short, I use protobuf Haskell library for working with protocol buffers. The library makes you forget about maintaining separate .proto files, it derives the way to serialise and deserialise your data type if it's an instance of Encode and Decode type classes respectively.

I'm designing a simple protocol on top of protobuf. There's a main message data type containing a message id, message type and some optional data that depends on the type. I want to have a function that gets message id, message type and the optional data (which is required to be an instance of Encode class) and creates a new instance of MyMessage with optional data serialised to ByteString in advance.

That's how I try to do this:

{-# LANGUAGE DataKinds     #-}
{-# LANGUAGE DeriveGeneric #-}

import           Data.ByteString      (ByteString)
import           Data.Int
import           Data.Maybe
import           Data.Monoid
import           Data.ProtocolBuffers
import           Data.Serialize
import           Data.Text            (Text, pack)
import           GHC.Generics         (Generic)
import           GHC.TypeLits

data MyMsgType = MSG_TYPE_1
               | MSG_TYPE_2
               deriving (Enum, Show)

data MyMessage = MyMessage {
    msgId   :: Required 1 (Value Int64)
  , msgType :: Required 2 (Enumeration MyMsgType)
  , cpData  :: Optional 3 (Value ByteString)
} deriving (Generic, Show)

instance Encode MyMessage
instance Decode MyMessage

createMessage :: Encode a => Int64 -> MyMsgType -> Maybe a -> MyMessage
createMessage msgId msgType optionalData =
  MyMessage (putField msgId) (putField msgType) (putField $ serialiseOptionalData optionalData)

serialiseMessage :: MyMessage -> ByteString
serialiseMessage = runPut . encodeLengthPrefixedMessage

serialiseOptionalData :: Encode a => Maybe a -> Maybe ByteString
serialiseOptionalData Nothing    = Nothing
serialiseOptionalData (Just ctx) =
  Just . runPut . encodeLengthPrefixedMessage $ ctx

When I try using createMessage function I get the following error:

λ> createMessage 1 MSG_TYPE_1 Nothing

<interactive>:7:1:
    No instance for (Encode a0) arising from a use of ‘createMessage’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Encode MyMessage -- Defined at test.hs:24:10
      instance Encode
                 (unordered-containers-0.2.6.0:Data.HashMap.Base.HashMap
                    protobuf-0.2.1.0:Data.ProtocolBuffers.Wire.Tag
                    [protobuf-0.2.1.0:Data.ProtocolBuffers.Wire.WireField])
        -- Defined in ‘protobuf-0.2.1.0:Data.ProtocolBuffers.Encode’
    In the expression: createMessage 1 MSG_TYPE_1 Nothing
    In an equation for ‘it’: it = createMessage 1 MSG_TYPE_1 Nothing

Can someone explain me why this error arises and how to fix it? I don't get where exactly the compiler sees the ambiguity.

1

1 Answers

6
votes

You need to give more information about what the type of your optional data is. When you use Nothing as a value of type Maybe a, the compiler cannot infer what type exactly a is. You can fix it by explicitly giving a type to Nothing, as in (Nothing :: Maybe Foo).