如何为带有幻像类型变量的新类型定义MonadUnliftIO实例? [英] How to define MonadUnliftIO instance for a newtype with a phantom type-variable?

查看:78
本文介绍了如何为带有幻像类型变量的新类型定义MonadUnliftIO实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

相关问题-导出MonadThrow是否安全? ,MonadCatch,MonadBaseControl,MonadUnliftIO等?-我已启用的位置-DeriveAnyClassGeneralizedNewtypeDeriving 来编译代码,但不必费心看那些不祥的警告.现在,我正在运行重构的代码,它会引发运行时错误:

Related question - Is it safe to derive MonadThrow, MonadCatch, MonadBaseControl, MonadUnliftIO, etc? - where I had enabled, both - DeriveAnyClass and GeneralizedNewtypeDeriving to get the code to compile, but didn't bother looking at the ominous warnings. Now, that I am running my refactored code, it's throwing a runtime error:

No instance nor default method for class operation >>=

因此,我删除了DeriveAnyClass并仅保留了GeneralizedNewtypeDeriving并出现以下编译错误:

So, I removed DeriveAnyClass and kept ONLY GeneralizedNewtypeDeriving and have the following compile error:

{-# LANGUAGE DataKinds, GADTs, ScopedTypeVariables, TypeFamilies, AllowAmbiguousTypes, RankNTypes, StandaloneDeriving, UndecidableInstances #-}

newtype AuthM (fs :: [FeatureFlag]) auth m a =
  AuthM (ReaderT (Auth auth) m a)
  deriving (Functor, Applicative, Monad, MonadReader (Auth auth), MonadIO, MonadThrow, MonadCatch, MonadMask, MonadUnliftIO)


--     • Couldn't match representation of type ‘m (Control.Monad.IO.Unlift.UnliftIO
--                                                   (AuthM fs auth m))’
--                                with that of ‘m (Control.Monad.IO.Unlift.UnliftIO
--                                                   (ReaderT (Auth auth) m))’
--         arising from the coercion of the method ‘Control.Monad.IO.Unlift.askUnliftIO’
--           from type ‘ReaderT
--                        (Auth auth)
--                        m
--                        (Control.Monad.IO.Unlift.UnliftIO (ReaderT (Auth auth) m))’
--             to type ‘AuthM
--                        fs auth m (Control.Monad.IO.Unlift.UnliftIO (AuthM fs auth m))’
--       NB: We cannot know what roles the parameters to ‘m’ have;
--         we must assume that the role is nominal
--     • When deriving the instance for (MonadUnliftIO (AuthM fs auth m))
--    |
-- 82 |   deriving (Functor, Applicative, Monad, MonadReader (Auth auth), MonadIO, MonadThrow, MonadCatch, MonadMask, MonadUnliftIO)
--    |                                                                                                               ^^^^^^^^^^^^^

注意:我意识到有关>>=的第一个错误与有关MonadUnliftIO的错误无关.我已经确认,关闭DeriveAnyClass时,没有关于缺少>>=的警告.

Note: I realise that the first error about >>= has got nothing to do with the error about MonadUnliftIO. I have confirmed that there are no warnings about a missing >>=, when DeriveAnyClass is turned off.

我想我需要自己编写MonadUnliftIO的实例,因为在newtype和幻像类型变量存在的情况下,编译器可能无法解决这个问题.但是,我只是不知道如何定义

I guess I need to write the instance for MonadUnliftIO myself, because the compiler probably cannot figure this out in the presence of a newtype AND a phantom type-variable. However, I just can't figure out how to define the askUnliftIO for my type, given above.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Try13 where

import Control.Monad.Reader
import UnliftIO
import Control.Monad.Catch

data Auth = Auth

newtype AuhM m a = AuthM (ReaderT Auth m a)
  deriving(Functor, Applicative, Monad, MonadReader Auth, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadUnliftIO)

--     • Couldn't match representation of type ‘m (UnliftIO (AuhM m))’
--                                with that of ‘m (UnliftIO (ReaderT Auth m))’
--         arising from the coercion of the method ‘askUnliftIO’
--           from type ‘ReaderT Auth m (UnliftIO (ReaderT Auth m))’
--             to type ‘AuhM m (UnliftIO (AuhM m))’
--       NB: We cannot know what roles the parameters to ‘m’ have;
--         we must assume that the role is nominal
--     • When deriving the instance for (MonadUnliftIO (AuhM m))
--    |
-- 12 |   deriving(Functor, Applicative, Monad, MonadReader Auth, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadUnliftIO)
--    |                                                                                                       ^^^^^^^^^^^^^
-- 

推荐答案

计划:

  • 如何手动实施MonadUnliftIO.
  • 如何衍生新类型MonadUnliftIO.
  • How to implement MonadUnliftIO by hand.
  • How to newtype-derive MonadUnliftIO.
newtype AuthM m a = AuthM { unAuthM :: ReaderT Auth m a }
  deriving ...

instance MonadUnliftIO m => MonadUnliftIO (AuthM m) where
  askUnliftIO = AuthM (fmap (\(UnliftIO run) -> UnliftIO (run . unAuthM)) askUnliftIO)
  withRunInIO go = AuthM (withRunInIO (\k -> go (k . unAuthM)))

这没有什么不可思议的.这是导出askUnliftIO定义的方法.我们想将现有的MonadUnliftIO实例包装为ReaderT Auth m.使用该实例,我们可以:

There is nothing magical about this; here's how you can derive the definition of askUnliftIO. We want to wrap the existing instance of MonadUnliftIO for ReaderT Auth m. Using that instance, we have:

askUnliftIO :: ReaderT Auth m (UnliftIO (ReaderT Auth m))

我们正在寻找

_ :: AuthM m (UnliftIO (AuthM m))

换句话说,我们要用AuthM替换两次出现的ReaderT Auth.外层很容易:

In other words, we want to replace the two occurrences of ReaderT Auth with AuthM. The outer one is easy:

AuthM askUnliftIO :: AuthM m (UnliftIO (ReaderT Auth m))

要获取内部函数,可以使用fmap,然后问题就出在要找到正确的函数UnliftIO (ReaderT Auth m) -> UnliftIO (AuthM m).

To get at the inner one, we can use fmap, and then the problem becomes to find the right function UnliftIO (ReaderT Auth m) -> UnliftIO (AuthM m).

fmap _ (AuthM askUnliftIO) :: AuthM m (UnliftIO (AuthM m))

-- provided --

_ :: UnliftIO (ReaderT Auth m) -> UnliftIO (AuthM m)

我们现在正在寻找一个函数,并且库没有在UnliftIO上提供任何函数,因此,唯一的启动方法是具有模式匹配的lambda,并且由于函数结果为UnliftIO,我们也可以从构造函数开始:

We're now looking for a function, and the library doesn't provide any functions on UnliftIO, so the only way to start is a lambda with pattern-matching, and since the function result is UnliftIO, we can also start with a constructor:

(\(UnliftIO run) -> UnliftIO (_ :: forall a. AuthM m a -> IO a) :: UnliftIO (AuthM m))
  :: UnliftIO (ReaderT Auth m) -> UnliftIO (AuthM m)

-- where (run :: forall a. ReaderT Auth m a -> IO a)

在这里我们看到run和孔仅在其参数上有所不同.我们可以通过函数组成来转换函数的参数,我们用run . _填充孔,其中包含一个新孔:

Here we see that run and the hole only differ in their arguments. We can transform a function's argument by function composition, we fill the hole with run . _, containing a new hole:

(\(UnliftIO run) -> UnliftIO (run . (_ :: AuthM m a -> ReaderT Auth m a)
                                :: forall a. AuthM m a -> IO a
                             )
) :: UnliftIO (ReaderT Auth m) -> UnliftIO (AuthM m)

那个洞终于被析构函数\(AuthM u) -> u充满了. unAuthM.将所有片段放在一起:

That hole is finally filled with the destructor \(AuthM u) -> u, aka. unAuthM. Put all the pieces together:

fmap (\(UnliftIO run) -> UnliftIO (run . unAuthM)) (AuthM askUnliftIO)

请注意,fmap f (AuthM u) = AuthM (fmap f u)(根据AuthMfmap定义),这是在顶部获得版本的方式.是否进行这种重写主要取决于品味.

Note that fmap f (AuthM u) = AuthM (fmap f u) (by definition of fmap for AuthM), which is how you get the version at the top. Whether or not to do that bit of rewriting is mostly a matter of taste.

这些步骤中的大多数都可以在GHC的型孔的帮助下完成.当您尝试为表达式找到合适的形状时,一开始会有一些松散的结局,但也可能有一种使用类型化的孔来帮助探索的那部分的方法.

Most of these steps can be carried out with the help of GHC's typed holes. There's some loose ends at the beginning when you try to find the right shape for the expression, but there might also be a way to use typed holes to help with that part of the exploration as well.

请注意,这些都不要求对askUnliftIOAuthM的用途有任何了解.在AuthMReaderT之间进行100%的无意识包装/展开,即可以自动化的100%样板,这是下一节的主题.

Note that none of this requires any knowledge about the purpose of askUnliftIO nor AuthM. It's 100% mindless wrapping/unwrapping between AuthM and ReaderT, i.e., 100% boilerplate that could be automated, which is the topic of this next section.

为什么派生不起作用的技术解释.扩展名GeneralizedNewtypeDeriving试图将ReaderT Auth m (UnliftIO (ReaderT Auth m))强制转换为AuthM m (UnliftIO (AuthM m))(在askUnliftIO的情况下).但是,如果m名义上取决于其参数,则不可能.

Technical explanation of why deriving doesn't Just Work. The extension GeneralizedNewtypeDeriving tries to coerce ReaderT Auth m (UnliftIO (ReaderT Auth m)) to AuthM m (UnliftIO (AuthM m)) (in the case of askUnliftIO). However, this is not possible if m depends on its argument nominally.

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆