我有一個帶有默認實作的型別類,如果用戶想要使用他們的自定義 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’
當型別類具有型別系列時,如何讓派生 via子句進行編譯?
uj5u.com熱心網友回復:
正如錯誤訊息所說,您的關聯型別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://www.uj5u.com/gongcheng/400947.html
下一篇:Haskell嵌套函式順序
