2

I am trying to understand how this very abstract recursive function from the Haskell recursion-schemes package works (or, indeed, what it does!) - from this file:

class Functor (Base t) => Corecursive t where

  [...]

  -- | A generalized postpromorphism
  gpostpro
    :: (Recursive t, Monad m)
    => (forall b. m (Base t b) -> Base t (m b)) -- distributive law
    -> (forall c. Base t c -> Base t c)         -- natural transformation
    -> (a -> Base t (m a))                      -- a (Base t)-m-coalgebra
    -> a                                        -- seed
    -> t
  gpostpro k e g = a . return where a = embed . fmap (ana (e . project) . a . join) . k . liftM g

In particular, what I want to understand is: how does it apply the g function which mentions the monad type constructor m, but then return a value of the t type, which doesn't mention or depend on m? I thought escaping from arbitrary monads was impossible in Haskell!

I first loaded the source file into Intero to try to use its type-at-point feature, but that attempt failed.

I then loaded it into GHCi using cabal repl, and tried to follow the types through the composed functions one at a time, using GHCi to help with type inference, by commenting out various bits of the definition. However, when I got to the fmap, I couldn't work out what to comment out, because if I uncommented the recursive a invocation but commented out other stuff, I thought it probably wouldn't even compile because the partially-commented-out definition of a wouldn't have the right type.

Robin Green
  • 32,079
  • 16
  • 104
  • 187
  • 1
    You can place `_` instead any expression to ask GHC about its type. – arrowd Sep 25 '16 at 10:31
  • And wrt. escaping: `t` is of `Recursive` class, which means it has `* -> *` kind. This hints that gpospro return type isn't escaping anything and is wrapped into something. Which is probably a monad. – arrowd Sep 25 '16 at 10:36
  • 1
    @arrowd The above type signature would be impossible if that was the case because you cannot have a return type that has kind `* -> *`. – David Young Sep 25 '16 at 14:30

1 Answers1

2

I managed to get ghci to tell me what the types of subexpressions were by surrounding them in (...:: _).

Turns out, the trick is, the "distributive law" k allows you to shove the monad inside the temporary Base type, and then the embed method allows you to dispense with the temporary Base type and get back to t. If indeed the concrete type t doesn't mention IO, then it would be impossible to write such a k (safely) for the IO monad, for example. So there is no magic here - i.e. no way to use this function to escape from monads which would otherwise be unescapable, like IO.

Robin Green
  • 32,079
  • 16
  • 104
  • 187