如何使用Data.Constraint来约束约束?

时间:2014-01-21 15:31:46

标签: haskell

我有一个类型

的功能
(forall m. (MonadCo r m, MonadReader Int m) => m ())
    -> (forall m. (MonadCo r m, MonadReader Int m) => m ())

MonadCo是我自己的类型类代表一个协程monad。如果你愿意,你可以考虑案例MonadError e m。问题将是相同的。)

似乎我应该能够重新启用约束并最终得到一个带有类型签名的函数,如

(Equals k (MonadCo r, MonadReader Int))
    => (Constrain k ()) -> (Constrain k ())

但我不知道如何在实践中实现这一点。我对:-:=>实际上是什么感到困惑。我想我在那里也需要一个Forall1,因为我普遍量化m,但我不知道它应该适合它。

我想要做的就是重新启用forall m. (MonadCo r m, MonadReader Int m)约束。我认为,当我这样做时,左边出现的任何内容都将自动成为“正确的东西”。

Data.Constraint似乎非常强大,但我无法确定从哪里开始。

2 个答案:

答案 0 :(得分:1)

似乎不可能。看起来你的“Implies”在k中是逆变的,而你想要的功能则不会。

我的意思是,如果你有一些奇怪的课程MonadFoo m,你定义

type k m = (MonadCo r m, MonadReader Int m, MonadFoo m)

假设这个类是专门设计的,只有这个类的一个实例(如果你至少不使用GeneralizedNewtypeDeriving),那么一些monad MMM,这也是MonadCo rMonadReader Int的一个实例。然后,您的类型Constrain k ()可能与MMM ()同构。所以,你想要一个函数MMM () -> MMM () - 但是输入类型MMM ()比你的函数工作所需要的要弱得多。您不可能以这种方式概括您的功能。

<强>更新

一些快乐的Oleg'ing,我们有以下代码:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Forall where
import Control.Monad.Reader
class Monad m => MonadCo r m where
scary ::
    (forall m. (MonadCo r m, MonadReader Int m) => m ()) ->
    (forall m. (MonadCo r m, MonadReader Int m) => m ())
scary = undefined -- here goes your original function
class Equals (d1 :: (* -> *) -> *) d2 where
    equals :: p d2 -> p d1
    default equals :: p d1 -> p d1
    equals = id
data Dict k (m :: * -> *) where Dict :: k m => Dict k m
type Constraint d a = forall m. d m -> m a
data (d1 :+: d2) (m :: * -> *) = Plus (d1 m) (d2 m)
newtype ConstraintTrans d = ConstraintTrans {runCT :: Constraint d () -> Constraint d ()}
warmAndFuzzy ::
    forall r d.
    Equals d (Dict (MonadCo r) :+: Dict (MonadReader Int)) =>
    Constraint d () -> Constraint d ()
warmAndFuzzy =
    let scary' ::
            (MonadCo r m, MonadReader Int m)
            => (forall m'. (MonadCo r m', MonadReader Int m') => m' ())
                -> m ()
        scary' = scary
        waf :: Constraint (Dict (MonadCo r) :+: Dict (MonadReader Int)) () ->
               Constraint (Dict (MonadCo r) :+: Dict (MonadReader Int)) ()
        waf c (Plus Dict Dict) =
            let arg :: forall m'. (MonadCo r m', MonadReader Int m') => m' ()
                arg = c $ Plus Dict Dict
            in scary' arg
    in runCT $ equals $ ConstraintTrans waf

答案 1 :(得分:1)

这应该是一个好的开始:

type ReaderAndCo r m = (MonadCo r m, MonadReader Int m)

g :: (forall m . Dict (ReaderAndCo r m) -> m ())
  -> (forall m . Dict (ReaderAndCo r m) -> m ())
g x Dict = f (x Dict)

如果您愿意,可以将ReaderAndCo的字典进一步拆分为两个组件 愿望。

为了使其更类似于您问题中的代码,您可以引入一个额外的同义词:

type Constrain k a = forall m . Dict (k m) -> m a

g :: Constrain (ReaderAndCo r) () -> Constrain (ReaderAndCo r) ()
g x Dict = f (x Dict)

这是你想要达到的目标吗?