我有一个新类型
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 sym
,sym
是class
中唯一显示的位置。
现在实例化类
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。但如果你详细说明第二次尝试,我也会很感激!
答案 0 :(得分:4)
GHC不知道在sym
中为enterSc
选择哪种类型,因为它仅在约束中使用,而在其他任何地方都没有。
您可以通过功能依赖来解决此问题:
class STScopedTable st sym | st -> sym where
st_table :: Lens' st (ScopedTable sym)
这告诉GHC sym
类型在st
的所有实例中由STScopedTable
类型唯一确定。由于MonadState
具有功能依赖性,表示s
由m
唯一确定,我们也知道(由于传递性)sym
由m
唯一确定(我们最终工作的monad。)
避免使用pragma不一定是好事。我不认为可以在不启用任何编译指示的情况下消除您的第一个版本的歧义。在可能的情况下避免像IncoherentInstances
这样的某些编译指示是个好主意,但我不建议制定一个避免编译指示的一般规则。