6

我试图表达一个给定的想法

instance (MonadTrans t, MonadX m) => MonadX (t m)

它应该遵循,只要有实例,任何t1 (t2 ... (tn m))也是。但是,当我尝试将其写下来时,它不起作用:MonadXtxMonadTrans

{-# LANGUAGE BasicallyEverything #-}

data Dict c where
  Dict :: c => Dict c

class (forall m . Monad m => Monad (t m)) => MonadTrans t where
  lift :: Monad m => m a -> t m a

class    (c m, Monad m, forall t . (MonadTrans t, Monad (t m)) => c (t m)) => Foo c m
instance (c m, Monad m, forall t . (MonadTrans t, Monad (t m)) => c (t m)) => Foo c m

liftDict :: MonadTrans t => Dict (Foo c m) -> Dict (Foo c (t m))
liftDict Dict = Dict 

这会导致以下错误:

    • Could not deduce: c (t1 (t m)) arising from a use of ‘Dict’
      from the context: MonadTrans t
        bound by the type signature for:
                   liftDict :: forall (t :: (* -> *) -> * -> *)
                                      (c :: (* -> *) -> Constraint) (m :: * -> *).
                               MonadTrans t =>
                               Dict (Foo c m) -> Dict (Foo c (t m))
        at src/Lib.hs:85:1-64
      or from: Foo c m
        bound by a pattern with constructor:
                   Dict :: forall (a :: Constraint). a => Dict a,
                 in an equation for ‘liftDict’
        at src/Lib.hs:86:10-13
      or from: (MonadTrans t1, Monad (t1 (t m)))
        bound by a quantified context at src/Lib.hs:1:1
    • In the expression: Dict
      In an equation for ‘liftDict’: liftDict Dict = Dict
    • Relevant bindings include
        liftDict :: Dict (Foo c m) -> Dict (Foo c (t m))
          (bound at src/Lib.hs:86:1)
   |
86 | liftDict Dict = Dict 

有什么办法让它工作吗?

4

1 回答 1

5

使用这里给出的稍微简单的定义,您会得到完全相同的错误Foo c m

 (c m, Monad m, forall t. MonadTrans t => c (t m))
                                      ^^^ don't really need Monad (t m) here

让我们澄清一些变量名称,以便在编写时清楚哪些变量绝对相等,哪些不相等liftDict

有:

MonadTrans t
    forall m'. Monad m' => Monad (t m')
Foo c m
    c m
    Monad m
    forall t'. MonadTrans t' => c (t' m)

想:

Foo c (t m)
    c (t m)
    Monad (t m)
    forall t''. MonadTrans t'' => c (t'' (t m))

在“想要”类别中,c (t m)很容易——我们申请forall t'. MonadTrans t' => c (t' m)我们的t'~tand MonadTrans t。也很容易Monad (t m),通过应用和。forall m'. Monad m' => Monad (t m')m'~mMonad m

但是最后一个......很棘手!您希望这些排列:

Have: forall t' . MonadTrans t'  => c (t'  m    )
Want: forall t''. MonadTrans t'' => c (t'' (t m))

但他们并没有完全排成一行。这里发生的m是一个固定的单子,而不是我们可以专门针对我们的新选择的论点t m!好吧,让我们把它作为一个论据。

class    (c m, Monad m, forall m' t . (Monad m', MonadTrans t) => c (t m')) => Foo c m
instance (c m, Monad m, forall m' t . (Monad m', MonadTrans t) => c (t m')) => Foo c m

这编译!但它不再说明我们想要什么,因为我们在这里所说的归纳步骤不需要c约束。幸运的是,我们可以重新添加它而不会导致它停止编译:

class    (c m, Monad m, forall m' t . (c m', Monad m', MonadTrans t) => c (t m')) => Foo c m
instance (c m, Monad m, forall m' t . (c m', Monad m', MonadTrans t) => c (t m')) => Foo c m

我认为您可能会发现将这些约束稍微不同地分组是很直观的:

class ( (c m, Monad m) -- base case
      , forall m' t. (c m', Monad m', MonadTrans t) => c (t m')
      -- inductive hypothesis
      ) => Foo c m

但请注意:这Foo可能比您最初想象的要少。特别是,为了有一个Foo c实例,对于类型级应用程序,只能有一个完全通用的实例。c

于 2022-01-25T18:49:59.120 回答