Gabriella439 / Gabriella439/Haskell-MMorph-Library

`instance MFunctor t => MFunctor (Psi t) ...`

Open
#20 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
53
Forks
24
PR merge metrics
No merged PRs in 30d

Description

It seems I very frequently run into trouble with `MFunctor` instances that carry an `MFunctor` constraint forward. This is probably not a legitimate type, but consider:

```
newtype Stream t m r = Stream {runStream :: m (Either r (t m (Stream t m r)))}
```

Writing a functor instance is simple

```
instance (Monad m, Functor (t m)) => Functor (Stream t m) where
fmap f = Stream . liftM (either (Left . f) (Right . fmap (fmap f))) . runStream
```

but if I set out to write

```
instance (MFunctor t) => MFunctor (Stream t) where
hoist phi = loop where
loop (Stream me) = Stream $ phi $ do
e <- me
case e of
Left r -> return $ Left r
Right tr -> return $ Right $ hoist phi (fmap loop tr)
```

of course I get the complaint that

```
Could not deduce (Functor (t m)) arising from a use of ‘loop’
from the context (MFunctor t)
```

But the constraint can't be added, since `m` is not in scope. One can do an end run around this:

```
newtype Stream t m r = Stream {runStream :: m (Either r (t m (Stream t m r)))}

class RFunctor t where
rmap :: forall m a b . Monad m => (a -> b) -> t m a -> t m b

instance RFunctor t => RFunctor (Stream t) where
rmap f = Stream . liftM (either (Left . f) (Right . rmap (rmap f))) . runStream

instance (Monad m, RFunctor t) => Functor (Stream t m) where fmap = rmap

instance (MFunctor t, RFunctor t) => MFunctor (Stream t) where
hoist phi = loop where
loop (Stream me) = Stream $ phi $ do
e <- me
case e of
Left r -> return $ Left r
Right tr -> return $ Right $ hoist phi (rmap loop tr)
```

Am I missing some simpler device? I have bumped to this more than once; maybe here I have a bad instance, but it will it think be a distraction to meditate on the particular case. It is fairly typical that you need a stronger fmap where you write `instance (MFunctor t) => MFunctor (... t ...)` since you can't presuppose `(forall m. Monad m => Functor (t m))`

It occurred to me that something like `RFunctor` (pardon the dumb name) could be a constraint on `MFunctor` or the slightly strengthened `fmap` could be included in its methods. But I don't know.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the Stream, MFunctor, and proposed RFunctor examples in the issue to understand the constraint problem. Determine whether the library’s typeclass API should express the stronger mapping requirement; done requires an agreed design, documented behavior, and any corresponding implementation or tests identified by the maintainers.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.