用 newtype 包装一个类型

Wrap a type in newtype

在下面的代码中我得到了警告 Orphan instance: instance (MonadIO m, Monad m) => GenerateUUID m

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

根据它的解决方案是

        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.

(或者禁用警告帽网上也有建议)

我的问题是我找不到如何用新类型包装类型?

您可以像这样定义一个新类型包装器:

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

如果我们还想说 "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" 我们可以使用 -XDerivingVia 扩展名:

{-# LANGUAGE DerivingVia #-}

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

任何时候编译器抱怨缺少 Foo 的实例,将其添加到 deriving 子句。


略微改进。假设我们还想 "inherit" SemigroupMonoid 实例。例如:IO ()Monoid,所以我们可能希望 Foo IO () 也是 Monoid。我们需要一个单独的 deriving 子句:

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

为什么要有一个单独的子句?因为类型类有不同的种类。 Functor 有种类 (Type -> Type) -> Constraint,我们正在为 Foo m 声明实例,它有种类 Type -> Type.

与此同时,Semigroup 具有种类 Type -> Constraint,我们正在为 Foo m a 声明实例,"fully applied" 类型构造函数具有种类 Type