named icon indicating copy to clipboard operation
named copied to clipboard

Problem with type inference caused by `! defaults`

Open Unisay opened this issue 7 years ago • 2 comments

I have the following function declared in my typeclass: https://github.com/DSTOQ/haskell-stellar-client/blob/master/src/Control/Monad/Stellar.hs#L26

When calling it in this way:

lastAccountTransaction :: MonadStellar m => AccountId -> m Transaction
lastAccountTransaction accountId = do
  let txs = accountTransactions accountId
          ! #cursor Nothing
          ! #limit Nothing
          ! #order Nothing

all compiles well, but if only I try to use ! defaults, I get the following error:

      • Could not deduce (MonadStellar m0)
            arising from a use of ‘accountTransactions’
          from the context: MonadStellar m
            bound by the type signature for:
                       lastAccountTransaction :: forall (m :: * -> *).
                                                 MonadStellar m =>
                                                 AccountId -> m Transaction
            at src/Service/Domain/Offer.hs:40:1-70
          The type variable ‘m0’ is ambiguous
          These potential instances exist:
            instance Control.Monad.Rest.MonadRest m =>
                     MonadStellar (StellarT m)
              -- Defined in ‘Control.Monad.Trans.Stellar’
            instance MonadStellar m => MonadStellar (ExceptT e m)
              -- Defined in ‘Control.Monad.Stellar’
            instance MonadStellar m => MonadStellar (StateT s m)
              -- Defined in ‘Control.Monad.Stellar’
            ...plus four instances involving out-of-scope types
            (use -fprint-potential-instances to see them all)
        • In the first argument of ‘(!)’, namely
            ‘accountTransactions accountId’
          In the expression: accountTransactions accountId ! defaults
          In an equation for ‘txs’:
              txs = accountTransactions accountId ! defaults

Unisay avatar Sep 25 '18 12:09 Unisay

The problem is that the type of accountTransactions is polymorphic in its return type:

m ([TransactionDetails], Cursor)

This means that one of potential instantiations of m is (->) ("param" :? T), for example

    :: AccountId
    -> "cursor" :? Maybe Cursor
    -> "order"  :? Maybe Order
    -> "limit"  :? Maybe Int
    -> "param"  :? T
    -> ([TransactionDetails], Cursor)

and in this case ! defaults would fill in "param" :? T as Nothing.

This possibility means that ! defaults tries to inspect m, and this results in the cryptic error message that you see.

A workaround is to give ! defaults a clear signal to stop inspecting the rest of the type:

newtype M m a = M { runM :: m a }
  deriving newtype (Functor, Applicative, Monad)

  accountTransactions
    :: AccountId
    -> "cursor" :? Maybe Cursor
    -> "order"  :? Maybe Order
    -> "limit"  :? Maybe Int
    -> M m ([TransactionDetails], Cursor)

In this case, ! defaults would stumble on M and would not try to inspect the type any further.

int-index avatar Sep 25 '18 12:09 int-index

Thank you for the clarification!

Unisay avatar Sep 25 '18 12:09 Unisay