我正在尝试构建一个库,以根据某些上下文填充模板。
相关数据类型为ContextNode
和Context
。
data ContextNode m = ContextText Text
| ContextSub (Context m)
type Context m = (Text -> m (Maybe (ContextNode m)))
我定义了类型类ContextGenerator
,以便能够使用泛型来为数据类型派生某些上下文。
class ContextGenerator m a where
clookup :: a -> Text -> m (Maybe (ContextNode m))
Context
应该是ContextGenerator
instance (MonadIO m) => ContextGenerator m (Context m) where
clookup a s = a s
制作上下文的一些代码
mkContext :: MonadIO m => Text -> ContextNode m -> Context m
mkContext s n = \s' -> if s' == s then return (Just n) else return Nothing
不能正常工作的是当我执行以下操作时(在我启用OverloadedStrings和FlexibleContexts的repl中)
> let ctx = mkContext "hello" (ContextText "world")
> clookup ctx "hello"
Could not deduce (Control.Monad.IO.Class.MonadIO m0)
from the context (Control.Monad.IO.Class.MonadIO m1,
ContextGenerator m (Context m1))
bound by the inferred type for ‘it’:
(Control.Monad.IO.Class.MonadIO m1,
ContextGenerator m (Context m1)) =>
m (Maybe (ContextNode m))
at <interactive>:18:1-19
The type variable ‘m0’ is ambiguous
When checking that ‘it’ has the inferred type
it :: forall (m :: * -> *) (m1 :: * -> *).
(Control.Monad.IO.Class.MonadIO m1,
ContextGenerator m (Context m1)) =>
m (Maybe (ContextNode m))
Probable cause: the inferred type is ambiguous
在我看来,GHC推断实例定义中的两个m
是不同的,这是正确的吗?我怎么能告诉GHC这些应该是一样的呢?
答案 0 :(得分:4)
你可以在GHC中使用functional dependencies来完成这项工作 - 只需添加语言扩展并将你的类型类改写为:
class ContextGenerator m a | a -> m where ...
这基本上说a
应该包含m
的选择(你可以说ContextGenerator
现在不是m
和{{1}类型的二元关系但是来自a
的函数 - 也是扩展的语法所暗示的)
a -> m
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
import Data.Text
import Control.Monad.IO.Class
data ContextNode m = ContextText Text
| ContextSub (Context m)
type Context m = (Text -> m (Maybe (ContextNode m)))
class ContextGenerator m a | a -> m where
clookup :: a -> Text -> m (Maybe (ContextNode m))
instance (MonadIO m) => ContextGenerator m (Context m) where
clookup a s = a s
mkContext :: MonadIO m => Text -> ContextNode m -> Context m
mkContext s n = \s' -> if s' == s then return (Just n) else return Nothing
因为这些类型和类是相互依赖的,所以我不确定你在这里确实有类型类的用例 - 也许你可以在没有所有开销的情况下使用该函数吗?
λ> let ctx = mkContext "hello" (ContextText "world")
λ> (Just (ContextText t)) <- clookup ctx "hello"
λ> t
"world"
λ> Just _ <- clookup ctx "???"
*** Exception: user error (Pattern match failure in do expression at <interactive>:41:1-6)
λ> Nothing <- clookup ctx "???"
(...empty of course...)