2

In the following code I got warning Orphan instance: instance (MonadIO m, Monad m) => GenerateUUID m

instance (MonadIO m, Monad m) => GenerateUUID m where
  generateUUID = liftIO nextRandom

According to it the solution is either

        move the instance declaration to the module of the class or of the type, or
        wrap the type with a newtype and declare the instance on the new type.

(or disable the warning hat Internet also suggest)

My problem is that I'm not able to find how to wrap the type with a newtype?

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
majkrzak
  • 1,332
  • 3
  • 14
  • 30
  • 1
    Take a look at Learn You A Haskell for Great Good, to get some fundamentals about Haskell. –  Jun 12 '20 at 19:57
  • 2
    Where is GenerateUUID defined? – chepner Jun 12 '20 at 21:41
  • I’m pretty sure you shouldn’t be able to define an instance like this. If you do define an instance `GenerateUUID m`, then this will end up being an instance for _any_ type `m`, and so you won’t be able to define any more `GenerateUUID` instances as they will overlap with your maximally general `m` instance. (This is the case even if you give a constraint `(MonadIO m, Monad m)`, due to how GHC’s instance resolution works.) – bradrn Jun 13 '20 at 02:27
  • 2
    It seems `generateUUID` should be a top-level definition and not a method of a typeclass. Did you create this `GenerateUUID` typeclass? Or is it from a library? – 4castle Jun 13 '20 at 05:06

1 Answers1

3

You can define a newtype wrapper like this:

newtype Foo m a = Foo { unFoo :: m a }

If we also want to say "I want Foo to have the same instances for Functor, Monad, Applicative... that m has, the only difference being that the methods use Foo m a instead of m a" we can use the -XDerivingVia extension:

{-# LANGUAGE DerivingVia #-}

newtype Foo m a = Foo { unFoo :: m a } 
                deriving (Functor,Applicative,Monad,MonadIO,GenerateUUID) via m

Any time the compiler complains about a missing instance for Foo, add it to the deriving clause.


A slight refinement. Suppose we also wanted to "inherit" Semigroup and Monoid instances. For example: IO () is a Monoid, so we might want Foo IO () to be a Monoid as well. We need a separate deriving clause:

newtype Foo m a = Foo { unFoo :: m a } 
                deriving (Functor,Applicative,Monad,MonadIO) via m
                deriving (Semigroup,Monoid) via (m a)

Why a separate clause? Because the typeclasses have different kinds. Functor has kind (Type -> Type) -> Constraint and we are declaring the instance for Foo m, which has kind Type -> Type.

Meanwhile, Semigroup has kind Type -> Constraint and we are declaring the instance for Foo m a, the "fully applied" type constructor with kind Type.

danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • 2
    "inheriting" instances from the wrapped type can also be done with the `GeneralizedNewtypeDeriving` extension or the `deriving newtype` strategy https://downloads.haskell.org/ghc/latest/docs/html/users_guide/glasgow_exts.html?highlight=derivingstrategies#extension-DerivingStrategies but I prefer `DerivingVia` because it is both more explicit and more versatile. – danidiaz Jun 13 '20 at 09:29
  • I just noticed that even with wrapped type, warning is still present when I move "instance" to the diferent fille than Foo... – majkrzak Jun 13 '20 at 18:50
  • @majkrzak why do you need to do that? – danidiaz Jun 13 '20 at 19:07