完整代码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,但我无法弄清楚可能出现的问题。
要编译这段代码的任何指针吗?
答案 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
,因为Bool
和FromJSON
都有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 Item
为a
生成模拟(这是我们打算在模拟中使用的makeAction "AccessAction" [ts| MonadAccess Item |]
类型。)
FromJSON Item
生成的代码会创建一个FlexibleContexts
的上下文,除非您启用MonadAccess Item
语言扩展名,否则不允许使用该上下文。
现在,我们保证在a
内,所有Item
类型变量都引用相同的类型function(a, bx, e)
。