[Haskell-cafe] Problem with lifted-async

Arie Peterson ariep at xs4all.nl
Sat Sep 5 14:38:56 UTC 2015

> ex.hs:20:20:
>     Couldn't match type ‘StM t a0’ with ‘StM t a’
>     NB: ‘StM’ is a type function, and may not be injective
>     The type variable ‘a0’ is ambiguous
>     Expected type: Async (StM t a0)
>       Actual type: Async (StM t a)
>     Relevant bindings include
>       as :: Async (StM t a) (bound at ex.hs:19:30)
>       t :: Task t a (bound at ex.hs:19:28)
>       td :: TaskDescriptor t (bound at ex.hs:19:10)
>       pollTask :: TaskDescriptor t
>                   -> TaskDescriptor t -> t (TaskDescriptor t)
>         (bound at ex.hs:19:1)
>     In the first argument of ‘poll’, namely ‘as’
>     In a stmt of a 'do' block: st <- poll as
> Failed, modules loaded: none.

A working version can be found at the bottom of this message: essentially you 
need to explain to GHC that the 'a' and 'a0' from the above error message are 
in fact the same.

The problem is indeed, as GHC suggests, that the StM type family might not be 
injective. The signature of 'poll' is

  poll :: forall m a0. MonadBaseControl IO m => Async (StM m a0) -> m (Maybe 
(Either SomeException a0))

Now, 'poll' is fed the argument 'as', which has type Async (StM m a), where 
'a' is from opening the existential TaskDescriptor type. GHC combines this 
with the type of 'poll', and concludes that Async (StM m a) ~ Async (StM m 
a0). Now, we would like to conclude from this that a ~ a0, but this is not a 
valid conclusion, exactly because of this possible non-injectivity of StM. 
Because the return value of 'poll' is not used quite that fully, there is no 
way for the compiler to infer what the type variable 'a0' should be 
instantiated with.

Another way to fix this is to use the return value of 'poll' in such a way that 
'a0' must equal 'a', for example by replacing the line

  Just r -> runTask t


  Just r -> runTask (case r of Left _ -> t; Right a -> Task (return a))

That way, you don't need the below scoped type variables workaround.

(BTW: perhaps the LHS of 'pollTask' should be

  pollTask td@(TaskDescriptor t as)

, with the @ ? Doesn't really matter for the problem at hand, though.)



==== ✂ ====
{-# LANGUAGE ExistentialQuantification  #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE ScopedTypeVariables        #-}

import Control.Concurrent.Async.Lifted (Async,async,asyncBound,poll,cancel)
import Control.Monad.Trans.Control (MonadBaseControl,StM)
import Control.Exception.Base (SomeException)

data Task m a = Task {
    taskVal   :: m a

data TaskDescriptor m = forall a. TaskDescriptor {
    task        :: Task m a,
    asyncThread :: Async (StM m a)

runTask :: forall a m. (MonadBaseControl IO m) => Task m a -> m 
(TaskDescriptor m)
runTask = undefined

pollTask td(TaskDescriptor (t :: Task m a) as) = do
        st :: Maybe (Either SomeException a) <- poll as
        case st of
            Nothing -> return td
            Just r -> runTask t

main = undefined
==== ✂ ====

More information about the Haskell-Cafe mailing list