无法将类型'a'与'b'匹配

时间:2017-11-25 14:14:11

标签: haskell

完整代码http://codepad.org/of3mTarF

我正在尝试使用monad-mock来测试我的代码,但是我遇到了一个我不知道如何解决的问题。

我正试图模仿的monad:

msg := <-messages
    r := msg.GetBody("BODY[]")

    if r == nil {
        log.Fatal("Server didn't returned message body")
    }

我还有一个根据monad定义的函数:

class Monad m => MonadAccess m where
  getItem :: FromJSON a => ItemId -> m a
  getNextPage :: (FromJSON a, Show a, Eq a) => Pager a -> m (Maybe (Pager a))

我使用TemplateHaskell定义模拟:

getAllPages
  :: (MonadAccess m, FromJSON a, Show a, Eq a)
  => String
  -> m [a]
getAllPages s = do
    firstPage <- getItem s
    go (pagerItems firstPage) (pagerNext firstPage)
  where
    go acc mp =
      case mp of
        Nothing -> return acc
        Just p  -> getNextPage p >>= go (acc ++ pagerItems p)

但即使我手动定义了所有必要的位,我得到的错误也是一样的:

makeAction "AccessAction" [ts| MonadAccess |]

我尝试从mock.hs:65:1: error: • Couldn't match type ‘a’ with ‘b’ ‘a’ is a rigid type variable bound by the type signature for: eqAction :: forall a b. AccessAction a -> AccessAction b -> Maybe (a Data.Type.Equality.:~: b) at mock.hs:65:1 ‘b’ is a rigid type variable bound by the type signature for: eqAction :: forall a b. AccessAction a -> AccessAction b -> Maybe (a Data.Type.Equality.:~: b) at mock.hs:65:1 Expected type: Maybe (a Data.Type.Equality.:~: b) Actual type: Maybe (a Data.Type.Equality.:~: a) • In the expression: Just Data.Type.Equality.Refl In the expression: if (&&) ((==) x_amxO y_amxP) True then Just Data.Type.Equality.Refl else Nothing In an equation for ‘eqAction’: eqAction (GetItem x_amxO) (GetItem y_amxP) = if (&&) ((==) x_amxO y_amxP) True then Just Data.Type.Equality.Refl else Nothing • Relevant bindings include eqAction :: AccessAction a -> AccessAction b -> Maybe (a Data.Type.Equality.:~: b) (bound at mock.hs:65:1) mock.hs:65:1: error: • Could not deduce: a2 ~ a1 from the context: (a ~ Maybe (Pager a1), FromJSON a1, Show a1, Eq a1) bound by a pattern with constructor: GetNextPage :: forall a_Xe30. (FromJSON a_Xe30, Show a_Xe30, Eq a_Xe30) => Pager a_Xe30 -> AccessAction (Maybe (Pager a_Xe30)), in an equation for ‘eqAction’ at mock.hs:65:1-45 or from: (b ~ Maybe (Pager a2), FromJSON a2, Show a2, Eq a2) bound by a pattern with constructor: GetNextPage :: forall a_Xe30. (FromJSON a_Xe30, Show a_Xe30, Eq a_Xe30) => Pager a_Xe30 -> AccessAction (Maybe (Pager a_Xe30)), in an equation for ‘eqAction’ at mock.hs:65:1-45 ‘a2’ is a rigid type variable bound by a pattern with constructor: GetNextPage :: forall a_Xe30. (FromJSON a_Xe30, Show a_Xe30, Eq a_Xe30) => Pager a_Xe30 -> AccessAction (Maybe (Pager a_Xe30)), in an equation for ‘eqAction’ at mock.hs:65:1 ‘a1’ is a rigid type variable bound by a pattern with constructor: GetNextPage :: forall a_Xe30. (FromJSON a_Xe30, Show a_Xe30, Eq a_Xe30) => Pager a_Xe30 -> AccessAction (Maybe (Pager a_Xe30)), in an equation for ‘eqAction’ at mock.hs:65:1 Expected type: Pager a1 Actual type: Pager a2 • In the second argument of ‘(==)’, namely ‘y_amxR’ In the first argument of ‘(&&)’, namely ‘(==) x_amxQ y_amxR’ In the expression: (&&) ((==) x_amxQ y_amxR) True • Relevant bindings include y_amxR :: Pager a2 (bound at mock.hs:65:1) x_amxQ :: Pager a1 (bound at mock.hs:65:1) 的定义中删除Maybe,但它没有改变任何内容。我也调查了Data.Type.Equality,但我无法弄清楚可能出现的问题。

要编译这段代码的任何指针吗?

1 个答案:

答案 0 :(得分:1)

好的,让我们再试一次。如果我们使用-ddump-splices开关,并稍微按下输出,我们会看到makeAction生成的内容:

data AccessAction r
  where
    GetItem :: FromJSON a => ItemId -> AccessAction a
    GetNextPage :: (FromJSON a, Show a, Eq a) =>
                   Pager a -> AccessAction (Maybe (Pager a))
deriving instance Eq (AccessAction r)
deriving instance Show (AccessAction r)

instance Action AccessAction where
  eqAction (GetItem x) (GetItem y)
    = if x == y then
          Just Refl
      else
          Nothing
  eqAction (GetNextPage x) (GetNextPage y)
    = if x == y True then
          Just Refl
      else
          Nothing
  eqAction _ _ = Nothing

instance Monad m =>
         MonadAccess (MockT AccessAction m) where
  getItem x = mockAction "getItem" (GetItem x)
  getNextPage x = mockAction "getNextPage" (GetNextPage x)

eqAction AccessAction Action实例创建eqAction :: AccessAction a -> AccessAction b -> Maybe (a :~: b) 函数时发生错误。此实例的函数具有类型:

AccessAction

这意味着它将传递两个具有可能不同结果类型的Nothing,并且如果两个操作不同则必须返回Just Refl,或者如果两个操作相同则必须返回AccessAction (意味着它们具有相同的数据构造函数,相等的参数和匹配的返回类型)。

GetItem :: FromJSON a => ItemId -> AccessAction a GetNextPage :: (FromJSON a, Show a, Eq a) => Pager a -> AccessAction (Maybe (Pager a)) 构造函数的类型为:

eqAction

Just Refl函数模式在构造函数上匹配,检查参数是否相等,如果是,则返回Refl。但是,a :~: b居住a ~ b iff a ~ b,但没有约束可以保证GetItem,从而导致错误。例如,一个AccessAction Int可以是AccessAction Bool,另一个Int,因为BoolFromJSON都有FromJSON个实例。< / p>

您需要证明两个AccessAction实例的类型相同。我可以看到执行此操作的唯一方法是TestEquality有一个a实例,这需要TestEquality还有一个a实例。

相反,我们可以使MonadAccess具有函数依赖的MonadState类型类的参数(就像FromJSON, Eq, and Show中的状态参数一样),并移动class (FromJSON a, Show a, Eq a, Monad m) => MonadAccess a m | m -> a where getItem :: ItemId -> m (Pager a) getNextPage :: Pager a -> m (Maybe (Pager a)) 约束进入类型类:

FunctionalDependencies

这要求我们添加getItem语言扩展名。

请注意,我还将m a的返回类型从m (Pager a)更改为getAllPages,这是在实践中使用它的方式(如果类型过于笼统,则编译会失败)

getAllPages :: MonadAccess a m => String -> m [a] 的类型现在更改为:

makeAction

我们最终可以使用MonadAccess Itema生成模拟(这是我们打算在模拟中使用的makeAction "AccessAction" [ts| MonadAccess Item |] 类型。)

FromJSON Item

生成的代码会创建一个FlexibleContexts的上下文,除非您启用MonadAccess Item语言扩展名,否则不允许使用该上下文。

现在,我们保证在a内,所有Item类型变量都引用相同的类型function(a, bx, e)