named
named copied to clipboard
Problem with type inference caused by `! defaults`
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
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.
Thank you for the clarification!