首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在类型家族中使用“派生通过”

在类型家族中使用“派生通过”
EN

Stack Overflow用户
提问于 2021-12-31 22:06:35
回答 1查看 140关注 0票数 0

我有一个具有默认实现的类型,如果用户想使用他们的自定义monad,我希望提供一种简单的方法来派生类型。

以下是其他人给我的解决方案:

代码语言:javascript
复制
{-# 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
代码语言:javascript
复制
> program
"Hello world!"

如果我的类型类使用类型家族,我就无法使用通过派生的。例如:

代码语言:javascript
复制
{-# 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

最后一行没有编译:

代码语言:javascript
复制
[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’

当类型集具有类型家族时,如何使通过子句派生的编译?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-01 21:35:28

正如错误消息所述,关联的类型FooCtx仅依赖于ctx,而不依赖于m,因此可能产生如下歧义:

代码语言:javascript
复制
instance MonadFoo X A where
  type FooCtx X = Int
  ...

instance MonadFoo X B where
  type FooCtx X = String
  ...

现在,FooCtx X的计算结果是Int还是String还不清楚。

要解决这个问题,只需将m添加到FooCtx的参数中即可。

代码语言:javascript
复制
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) = ()
  ...

(我想我应该把这句话作为回答,因为它毕竟是那么简单)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70545923

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档