我正在尝试编写一些 Scala 代码以实现mtl 风格的自定义行为。例如,为了公开对特定效果进行抽象的“写入数据库”功能,我编写了自己的类型类:
trait CanPersist[M[_]]:
def persistToDB[A](a: A): M[Unit]
given CanPersist[IO] with
def persistToDB[A](a: A): IO[Unit] = IO(???) // Write to DB
Run Code Online (Sandbox Code Playgroud)
IO 实例可以轻松实现,但我感兴趣的是自动为任何基于 IO 的 monad 堆栈提供实例:
// If a Transformer wraps a Monad that can persist then it can persist too
given persistTA[M[_]: CanPersist: Monad, T[_[_], _]: MonadTransformer]:
CanPersist[[A] =>> T[M, A]] with
def persistToDB[A](a: A): T[M, Unit] =
summon[MonadTransformer[T]].lift(summon[CanPersist[M]].persistToDB(a))
Run Code Online (Sandbox Code Playgroud)
问题显然是 cats 没有定义自己的MonadTransformer类型类;幸运的是,自己编写非常简单:
trait MonadTransformer[T[_[_], _]]:
def lift[M[_]: Monad, A](ma: M[A]): T[M, A] …Run Code Online (Sandbox Code Playgroud) functional-programming scala monad-transformers scala-cats given
作为练习,我一直在重新实现一些常见的 monad 及其相应的转换器;以下是我定义的一些类型:
newtype Writer w a = Writer { runWriter :: (w, a) }
newtype WriterT w m a = WriterT { runWriterT :: m (Writer w a) }
newtype Maybe a = Just a | Nothing
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Run Code Online (Sandbox Code Playgroud)
据我所知,变压器将一个单子“包裹”在一个外部单子中m;遵循这种直觉,我尝试ReaderT以类似的方式定义变压器:
newtype Reader r a = Reader { runReader :: r -> a }
newtype ReaderT r m a = ReaderT { runReaderT :: m …Run Code Online (Sandbox Code Playgroud)