我有一个具有默认实现的类型,如果用户想使用他们的自定义monad,我希望提供一种简单的方法来派生类型。
以下是其他人给我的解决方案:
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
import Control.Monad.Cont (MonadIO, MonadTrans (lift))
import Control.Monad.Reader (MonadReader, ReaderT (runReaderT))
----------------- My module's definitions -----------------
class Monad m => MonadFoo m where
foo :: m ()
instance MonadFoo IO where
foo = putStrLn "Hello world!"
instance MonadFoo m => MonadFoo (ReaderT r m) where
foo = lift foo
------------------------------------------------------------
------ The user's custom monad + instance definitions ------
data AppEnv = AppEnv
newtype AppM a = AppM
{ runAppM :: ReaderT AppEnv IO a
}
deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv)
deriving via (ReaderT AppEnv IO) instance MonadFoo AppM
------------------------------------------------------------
-- Example usage
program :: IO ()
program = runReaderT (runAppM foo) AppEnv> program
"Hello world!"如果我的类型类使用类型家族,我就无法使用通过派生的。例如:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.Cont (MonadIO, MonadTrans (lift))
import Control.Monad.Reader (MonadReader, ReaderT (runReaderT))
----------------- My module's definitions -----------------
class Monad m => MonadFoo ctx m where
type FooCtx ctx
foo :: m (FooCtx ctx)
data DummyCtx = DummyCtx
instance MonadFoo DummyCtx IO where
type FooCtx DummyCtx = ()
foo :: IO ()
foo = putStrLn "hello"
instance MonadFoo DummyCtx m => MonadFoo DummyCtx (ReaderT r m) where
type FooCtx DummyCtx = ()
foo :: ReaderT r m ()
foo = lift $ foo @DummyCtx
------------------------------------------------------------
------ The user's custom monad + instance definitions ------
data AppEnv = AppEnv
newtype AppM a = AppM
{ runAppM :: ReaderT AppEnv IO a
}
deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv)
deriving via (ReaderT AppEnv IO) instance MonadFoo DummyCtx AppM最后一行没有编译:
[typecheck] [E] • Can't make a derived instance of
~ ‘MonadFoo DummyCtx AppM’ with the via strategy:
~ the associated type ‘FooCtx’ is not parameterized over the last type
~ variable
~ of the class ‘MonadFoo’
~ • In the stand-alone deriving instance for ‘MonadFoo DummyCtx AppM’当类型集具有类型家族时,如何使通过子句派生的编译?
发布于 2022-01-01 21:35:28
正如错误消息所述,关联的类型FooCtx仅依赖于ctx,而不依赖于m,因此可能产生如下歧义:
instance MonadFoo X A where
type FooCtx X = Int
...
instance MonadFoo X B where
type FooCtx X = String
...现在,FooCtx X的计算结果是Int还是String还不清楚。
要解决这个问题,只需将m添加到FooCtx的参数中即可。
class Monad m => MonadFoo ctx m where
type FooCtx ctx m
...
instance MonadFoo DummyCtx IO where
type FooCtx DummyCtx IO = ()
...
instance MonadFoo DummyCtx m => MonadFoo DummyCtx (ReaderT r m) where
type FooCtx DummyCtx (ReaderT r m) = ()
...(我想我应该把这句话作为回答,因为它毕竟是那么简单)
https://stackoverflow.com/questions/70545923
复制相似问题