callServiceWithMaster :: forall a b. (RosBinary a, SrvInfo a, RosBinary b, SrvInfo b) =>
                         URI -> ServiceName -> a -> IO (Either ServiceResponseExcept b)
callServiceWithMaster rosMaster serviceName message = runExceptT $ do
  checkServicesMatch message (undefined::b)
  --lookup the service with the master
  (code, statusMessage, serviceUrl) <- lookupService rosMaster callerID serviceName
  checkLookupServiceCode code statusMessage
  (host, port) <- ExceptT . return $ parseHostAndPort serviceUrl
  -- make a socket
  let makeSocket = socket AF_INET Sock.Stream defaultProtocol
      closeSocket sock = liftIO $ sClose sock
      withSocket sock = do
        ioErrorToExceptT ConnectExcept "Problem connecting to server. Got exception : " $ do
          --Connect to the socket
          ip <- hostAddress <$> getHostByName host
          connect sock $ SockAddrInet port ip
        let reqMd5 = srvMD5 message
            reqServiceType = srvTypeName message
        negotiateService sock serviceName reqServiceType reqMd5
        let bytes = runPut $ putMsg 0 message
        ioErrorToExceptT SendRequestExcept "Problem sending request. Got exception: " $
          sendBS sock bytes
        liftIO $ socketToHandle sock ReadMode
  handle <- bracketOnErrorME (liftIO makeSocket) closeSocket withSocket
  result <- getServiceResult handle
  liftIO $ hClose handle
  return result
    where
      callerID = "roshask"
      checkLookupServiceCode 1 _ = return ()
      checkLookupServiceCode code statusMessage =
        throwError $ MasterExcept
        ("lookupService failed, code: " ++ show code ++ ", statusMessage: " ++ statusMessage)
      checkServicesMatch x y =
        unless match $
          -- throw error here since the calling code needs to be changed
          error "Request and response type do not match"
        where
          match = srvMD5 x == srvMD5 y && srvTypeName x == srvTypeName y

-- | bracketOnError equivalent for MonadError
callServiceWithMaster :: forall a b. (RosBinary a, SrvInfo a, RosBinary b, SrvInfo b) =>
                         URI -> ServiceName -> a -> IO (Either ServiceResponseExcept b)
callServiceWithMaster rosMaster serviceName message = runExceptT $ do
  checkServicesMatch message (undefined::b)
  --lookup the service with the master
  (code, statusMessage, serviceUrl) <- lookupService rosMaster callerID serviceName
  checkLookupServiceCode code statusMessage
  (host, port) <- ExceptT . return $ parseHostAndPort serviceUrl
  -- make a socket
  let makeSocket = socket AF_INET Sock.Stream defaultProtocol
      closeSocket sock = liftIO $ sClose sock
      withSocket sock = do
        ioErrorToExceptT ConnectExcept "Problem connecting to server. Got exception : " $ do
          --Connect to the socket
          ip <- hostAddress <$> getHostByName host
          connect sock $ SockAddrInet port ip
        let reqMd5 = srvMD5 message
            reqServiceType = srvTypeName message
        negotiateService sock serviceName reqServiceType reqMd5
        let bytes = runPut $ putMsg 0 message
        ioErrorToExceptT SendRequestExcept "Problem sending request. Got exception: " $
          sendBS sock bytes
        liftIO $ socketToHandle sock ReadMode
  handle <- bracketOnErrorME (liftIO makeSocket) closeSocket withSocket
  result <- getServiceResult handle
  liftIO $ hClose handle
  return result
    where
      callerID = "roshask"
      checkLookupServiceCode 1 _ = return ()
      checkLookupServiceCode code statusMessage =
        throwError $ MasterExcept
        ("lookupService failed, code: " ++ show code ++ ", statusMessage: " ++ statusMessage)
      checkServicesMatch x y =
        unless match $
          -- throw error here since the calling code needs to be changed
          error "Request and response type do not match"
        where
          match = srvMD5 x == srvMD5 y && srvTypeName x == srvTypeName y

-- | bracketOnError equivalent for MonadError
callServiceWithMaster rosMaster serviceName message = runExceptT $ do
  checkServicesMatch message (undefined::b)
  --lookup the service with the master
  (code, statusMessage, serviceUrl) <- lookupService rosMaster callerID serviceName
  checkLookupServiceCode code statusMessage
  (host, port) <- ExceptT . return $ parseHostAndPort serviceUrl
  -- make a socket
  let makeSocket = socket AF_INET Sock.Stream defaultProtocol
      closeSocket sock = liftIO $ sClose sock
      withSocket sock = do
        ioErrorToExceptT ConnectExcept "Problem connecting to server. Got exception : " $ do
          --Connect to the socket
          ip <- hostAddress <$> getHostByName host
          connect sock $ SockAddrInet port ip
        let reqMd5 = srvMD5 message
            reqServiceType = srvTypeName message
        negotiateService sock serviceName reqServiceType reqMd5
        let bytes = runPut $ putMsg 0 message
        ioErrorToExceptT SendRequestExcept "Problem sending request. Got exception: " $
          sendBS sock bytes
        liftIO $ socketToHandle sock ReadMode
  handle <- bracketOnErrorME (liftIO makeSocket) closeSocket withSocket
  result <- getServiceResult handle
  liftIO $ hClose handle
  return result
    where
      callerID = "roshask"
      checkLookupServiceCode 1 _ = return ()
      checkLookupServiceCode code statusMessage =
        throwError $ MasterExcept
        ("lookupService failed, code: " ++ show code ++ ", statusMessage: " ++ statusMessage)
      checkServicesMatch x y =
        unless match $
          -- throw error here since the calling code needs to be changed
          error "Request and response type do not match"
        where
          match = srvMD5 x == srvMD5 y && srvTypeName x == srvTypeName y

-- | bracketOnError equivalent for MonadError