[GHC] #11339: Possible type-checker regression in GHC 8.0

GHC ghc-devs at haskell.org
Sat Apr 16 17:02:52 UTC 2016


#11339: Possible type-checker regression in GHC 8.0
-------------------------------------+-------------------------------------
        Reporter:  hvr               |                Owner:  simonpj
            Type:  bug               |               Status:  new
        Priority:  highest           |            Milestone:  8.0.2
       Component:  Compiler (Type    |              Version:  8.0.1-rc1
  checker)                           |
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by RyanGlScott):

 This also affects [https://github.com/ekmett/machines machines].
 [https://github.com/ekmett/machines/blob/4999036bdefe286e940dc70bf83413724d3927d0/src/Data/Machine/Fanout.hs#L25-L56
 This is the code] that is affected (simplified version reproduced below):

 {{{#!hs
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 -- Simplified code from the machines package
 module Data.Machine.Fanout where

 class Semigroup a where
     (<>) :: a -> a -> a
     sconcat :: NonEmpty a -> a

 data NonEmpty a = a :| [a]

 -- | Witnessed type equality
 data Is a b where
   Refl :: Is a a

 -- | This is the base functor for a 'Machine' or 'MachineT'.
 --
 -- Note: A 'Machine' is usually constructed from 'Plan', so it does not
 need to be CPS'd.
 data Step k o r
   = Stop
   | Yield o r
   | forall t. Await (t -> r) (k t) r

 -- | A 'MachineT' reads from a number of inputs and may yield results
 before stopping
 -- with monadic side-effects.
 newtype MachineT m k o = MachineT { runMachineT :: m (Step k o (MachineT m
 k o)) }

 -- | A @'ProcessT' m a b@ is a stream transducer that can consume values
 of type @a@
 -- from its input, and produce values of type @b@ and has side-effects in
 the
 -- 'Monad' @m at .
 type ProcessT m a b = MachineT m (Is a) b

 continue :: ([b] -> r) -> [(a -> b, b)] -> Step (Is a) o r
 continue _ [] = Stop
 continue f ws = Await (f . traverse fst ws) Refl (f $ map snd ws)

 -- | Pack a 'Step' of a 'Machine' into a 'Machine'.
 encased :: Monad m => Step k o (MachineT m k o) -> MachineT m k o
 encased = MachineT . return

 semigroupDlist :: Semigroup a => ([a] -> [a]) -> Maybe a
 semigroupDlist f = case f [] of
   [] -> Nothing
   x:xs -> Just $ sconcat (x:|xs)

 -- | Share inputs with each of a list of processes in lockstep. Any
 -- values yielded by the processes are combined into a single yield
 -- from the composite process.
 fanout :: (Functor m, Monad m, Semigroup r)
        => [ProcessT m a r] -> ProcessT m a r
 fanout = MachineT . go id id
   where
     go waiting acc [] = case waiting [] of
       ws -> return . maybe k (\x -> Yield x $ encased k) $ semigroupDlist
 acc
         where k = continue fanout ws
     go waiting acc (m:ms) = runMachineT m >>= \v -> case v of
       Stop           -> go waiting acc ms
       Yield x k      -> go waiting (acc . (x:)) (k:ms)
       Await f Refl k -> go (waiting . ((f, k):)) acc ms

 -- | Share inputs with each of a list of processes in lockstep. If
 -- none of the processes yields a value, the composite process will
 -- itself yield 'mempty'. The idea is to provide a handle on steps
 -- only executed for their side effects. For instance, if you want to
 -- run a collection of 'ProcessT's that await but don't yield some
 -- number of times, you can use 'fanOutSteps . map (fmap (const ()))'
 -- followed by a 'taking' process.
 fanoutSteps :: (Functor m, Monad m, Monoid r)
             => [ProcessT m a r] -> ProcessT m a r
 fanoutSteps = MachineT . go id id
   where
     go waiting acc [] = case (waiting [], mconcat (acc [])) of
       (ws, xs) -> return . Yield xs $ encased (continue fanoutSteps ws)
     go waiting acc (m:ms) = runMachineT m >>= \v -> case v of
       Stop           -> go waiting acc ms
       Yield x k      -> go waiting (acc . (x:)) (k:ms)
       Await f Refl k -> go (waiting . ((f, k):)) acc ms
 }}}

 The workaround is to change the definitions of `fanout` and `fanoutSteps`
 to the following:

 {{{#!hs
 -- | Share inputs with each of a list of processes in lockstep. Any
 -- values yielded by the processes are combined into a single yield
 -- from the composite process.
 fanout :: forall m a r. (Functor m, Monad m, Semigroup r)
        => [ProcessT m a r] -> ProcessT m a r
 fanout = MachineT . go id id
   where
     go :: ([(a -> ProcessT m a r, ProcessT m a r)]
        -> [(a -> ProcessT m a r, ProcessT m a r)])
        -> ([r] -> [r])
        -> [ProcessT m a r]
        -> m (Step (Is a) r (ProcessT m a r))
     go waiting acc [] = case waiting [] of
       ws -> return . maybe k (\x -> Yield x $ encased k) $ semigroupDlist
 acc
         where k = continue fanout ws
     go waiting acc (m:ms) = runMachineT m >>= \v -> case v of
       Stop           -> go waiting acc ms
       Yield x k      -> go waiting (acc . (x:)) (k:ms)
       Await f Refl k -> go (waiting . ((f, k):)) acc ms

 -- | Share inputs with each of a list of processes in lockstep. If
 -- none of the processes yields a value, the composite process will
 -- itself yield 'mempty'. The idea is to provide a handle on steps
 -- only executed for their side effects. For instance, if you want to
 -- run a collection of 'ProcessT's that await but don't yield some
 -- number of times, you can use 'fanOutSteps . map (fmap (const ()))'
 -- followed by a 'taking' process.
 fanoutSteps :: forall m a r. (Functor m, Monad m, Monoid r)
             => [ProcessT m a r] -> ProcessT m a r
 fanoutSteps = MachineT . go id id
   where
     go :: ([(a -> ProcessT m a r, ProcessT m a r)]
        -> [(a -> ProcessT m a r, ProcessT m a r)])
        -> ([r] -> [r])
        -> [ProcessT m a r]
        -> m (Step (Is a) r (ProcessT m a r))
     go waiting acc [] = case (waiting [], mconcat (acc [])) of
       (ws, xs) -> return . Yield xs $ encased (continue fanoutSteps ws)
     go waiting acc (m:ms) = runMachineT m >>= \v -> case v of
       Stop           -> go waiting acc ms
       Yield x k      -> go waiting (acc . (x:)) (k:ms)
       Await f Refl k -> go (waiting . ((f, k):)) acc ms
 }}}

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/11339#comment:12>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list