7

I'm trying to create my own data type, which will be part of Monad class, but

newtype Container a = Container a deriving Monad

gives me this error:

   * Can't make a derived instance of `Monad Container'
        (even with cunning GeneralizedNewtypeDeriving):
        cannot eta-reduce the representation type enough
    * In the newtype declaration for `Container'
   |
30 | newtype Container a = Container a deriving Monad

It works fine for other classes (Show for example), but not for Monad, so how can I convince ghci to instance my Container to Monad class?

Thanks

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
Edward Grey
  • 101
  • 3
  • 1
    The problem is that `a` is not an instance of monad, hence it makes not much sense. If you for example would use `newtype Container a = Container [a] deriving (Functor, Applicative, Monad)` it will work, since `[]` is an instance of `Monad`. – Willem Van Onsem Feb 28 '20 at 18:37
  • 2
    `GenerlizedNewtypeDeriving` is specifically for "lifting" the wrapped type's instances to the new type. The question of how (or if) one can automatically derive a `Monad` instance for `Container` is still interesting. (The fact that `base` defines the `Monad` instance for `Identity` explicit suggests you cannot.) – chepner Feb 28 '20 at 19:04
  • `Monad` isn't one of the typeclasses that the Haskell standard makes available to be automatically derived (`Show` is, along with some other basic ones). GHC can do it with the correct extension(s) though, I believe. – Robin Zigmond Feb 28 '20 at 19:06
  • @RobinZigmond Note that the message indicates `GeneralizedNewtypeDeriving` is enabled, and a question is why it still doesn't work. – Alexey Romanov Feb 29 '20 at 06:07

1 Answers1

9

It works fine for other classes (Show for example)

Only a fixed set of standard classes support deriving out-of-the-box:

In Haskell 98, the only derivable classes are Eq, Ord, Enum, Ix, Bounded, Read, and Show. Various language extensions extend this list.

--- The GHC User Manual

In particular Monad does not belong to that list, nor the extended one.

There are more extensions that generalize deriving to arbitrary classes, but they cannot be 100% automated. Someone somewhere has to specify how that deriving is to be done; depending on the class, the user may be required to carry the burden because there is information that fundamentally cannot be inferred.

In your case, the newtype Container is representationally equivalent to the Identity monad in the standard library, so you can use DerivingVia:

{-# LANGUAGE DerivingVia #-}
import Data.Functor.Identity

newtype Container a = Container a deriving (Functor, Applicative, Monad) via Identity

There is only one sensible instance in this very particular situation, but most of the time it's not easy to tell what the instance should be even if there is only one.

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • You also have to derive `Functor` and `Applicative`, but then compare the type of `Container 3 >>= (+1)` with `Identity 3 >>= (+1)`. I don't know if that's related to `DerivingVia` or not. – chepner Feb 28 '20 at 19:43
  • (In case I'm doing something weird, I get `Container 3 >>= (+ 1) :: Num (Container b) => Container b` and `Identity 3 >>= (+ 1) :: Num b => Identity b`. I'm not sure why `Container b`, rather than `b`, has the `Num` constraint.) – chepner Feb 28 '20 at 19:51
  • Thanks for the precision. As for your second remark, to have `(+ 1) :: Num c => c -> c` as a Kleisli arrow `(+ 1) :: a -> Container b` you need to unify `c ~ Container b`. But I'm not sure what your point is to begin with. – Li-yao Xia Feb 28 '20 at 21:01
  • I'm just wondering what is defined for `Identity` that isn't defined for `Container`, as `Identity 3 >>= (+1)` evaluates to `Identity 4`. – chepner Feb 28 '20 at 21:05
  • It's just because there's an `instance Num a => Num (Identity a)` defined. – K. A. Buhr Feb 28 '20 at 21:07
  • Oh, duh. Thanks. – chepner Feb 28 '20 at 21:07