推断类型不明确

时间:2015-05-19 08:10:05

标签: haskell

我正在尝试构建一个库,以根据某些上下文填充模板。

相关数据类型为ContextNodeContext

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这些应该是一样的呢?

1 个答案:

答案 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...)