newtype类

时间:2015-10-08 00:59:47

标签: haskell ghc typeclass

我有一个新类型

newtype ScopedTable sym = ScopedTable { tab_stack :: [ Map String sym ] }

因为我希望为不同的符号类型提供类似符号表的结构。

在我第一次尝试时,我在一个monad中写了一个以通用方式使用ScopedTable的类,在类中我们只是让Lens专注于ScopedTable

class STScopedTable st where
        st_table :: Lens' st (ScopedTable sym)

请注意Lens'侧重于ScopedTable symsymclass中唯一显示的位置。

现在实例化类

data OpPState = OpPState { _opp_table :: ScopedTable Operator }
makeLenses ''OpPState

instance STScopedTable OpPState where
        st_table = opp_table

出现类型检查错误:

src/Language/Angler/MixfixParser.hs:74:20:
    Couldn't match type ‘sym’ with ‘Operator’
      ‘sym’ is a rigid type variable bound by
            the type signature for
              st_table :: Functor f =>
                          (ScopedTable sym -> f (ScopedTable sym)) -> OpPState -> f OpPState
            at src/Language/Angler/MixfixParser.hs:74:9
    Expected type: (ScopedTable sym -> f (ScopedTable sym))
                   -> OpPState -> f OpPState
      Actual type: (ScopedTable Operator -> f (ScopedTable Operator))
                   -> OpPState -> f OpPState
    Relevant bindings include
      st_table :: (ScopedTable sym -> f (ScopedTable sym))
                  -> OpPState -> f OpPState
        (bound at src/Language/Angler/MixfixParser.hs:74:9)
    In the expression: opp_table
    In an equation for ‘st_table’: st_table = opp_table

所以我尝试了MultiParamTypeClasses编译指示,现在sym必须传递给类:

class STScopedTable st sym where
        st_table :: Lens' st (ScopedTable sym)

但现在它不允许我正确使用类约束:

enterSc :: (STScopedTable s sym, MonadState s m) => m ()
enterSc = use st_table >>= assign st_table . enterScope

它给了我错误:

src/Language/Angler/Monad.hs:79:12:
    Could not deduce (STScopedTable s sym0)
    from the context (STScopedTable s sym, MonadState s m)
      bound by the type signature for
                 enterSc :: (STScopedTable s sym, MonadState s m) => m ()
      at src/Language/Angler/Monad.hs:79:12-56
    The type variable ‘sym0’ is ambiguous
    In the ambiguity check for the type signature for ‘enterSc’:
      enterSc :: forall (m :: * -> *) s sym.
                 (STScopedTable s sym, MonadState s m) =>
                 m ()
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
    In the type signature for ‘enterSc’:
      enterSc :: (STScopedTable s sym, MonadState s m) => m ()

我启用了AllowAmbiguousTypes pragma,现在我得到了:

src/Language/Angler/Monad.hs:81:15:
    Could not deduce (STScopedTable s sym0)
      arising from a use of ‘st_table’
    from the context (STScopedTable s sym, MonadState s m)
      bound by the type signature for
                 enterSc :: (STScopedTable s sym, MonadState s m) => m ()
      at src/Language/Angler/Monad.hs:80:12-56
    The type variable ‘sym0’ is ambiguous
    In the first argument of ‘use’, namely ‘st_table’
    In the first argument of ‘(>>=)’, namely ‘use st_table’
    In the expression: use st_table >>= assign st_table . enterScope

我可以为我在这个类中使用的每个monad编写enterSc,但这会破坏进行泛化的目的。

如果有人能给我一个如何解决第一次尝试的想法,我宁愿这样做,因为我总是喜欢使用较少的pragma。但如果你详细说明第二次尝试,我也会很感激!

1 个答案:

答案 0 :(得分:4)

GHC不知道在sym中为enterSc选择哪种类型,因为它仅在约束中使用,而在其他任何地方都没有。

您可以通过功能依赖来解决此问题:

class STScopedTable st sym | st -> sym where
        st_table :: Lens' st (ScopedTable sym)

这告诉GHC sym类型在st的所有实例中由STScopedTable类型唯一确定。由于MonadState具有功能依赖性,表示sm唯一确定,我们也知道(由于传递性)symm唯一确定(我们最终工作的monad。)

避免使用pragma不一定是好事。我不认为可以在不启用任何编译指示的情况下消除您的第一个版本的歧义。在可能的情况下避免像IncoherentInstances这样的某些编译指示是个好主意,但我不建议制定一个避免编译指示的一般规则。