Вот определение для MonadState
, но вопрос относится к любому такому классу с FunctionalDependencies
:
class Monad m => MonadState s m | m -> s where
...
Предположим, у меня есть тип данных, который использует s
в качестве аргумента типа и типа класс, который работает, работает с ним:
data StateType s = StateType
class MonadState s m => FunDeps s m a where
workWithStateType :: a -> StateType s -> m ()
Я могу с радостью создать экземпляр для этого класса, который компилируется и работает, как и ожидалось:
instance (MonadIO m, MonadState s m) => FunDeps s m (IORef (StateType s)) where
workWithStateType ref a = liftIO $ writeIORef ref a
Но мне кажется, что s
в FunDeps
класс является избыточным, и я мог бы определить класс следующим образом:
class FunDepsProblem m a where
workWithStateTypeNoCompile :: MonadState s m => a -> StateType s -> m ()
instance (MonadIO m, MonadState s m) => FunDepsProblem m (IORef (StateType s)) where
...
Проблема заключается в том, что когда я пытаюсь его реализовать:
instance (MonadIO m, MonadState s m) => FunDepsProblem m (IORef (StateType s)) where
workWithStateTypeNoCompile ref a = liftIO $ writeIORef ref a
Я получаю ошибку компиляции, которая говорит мне, что он не может унифицировать токен состояния s
в заголовке экземпляра и в функции:
fun-deps.hs:18:62: error: …
• Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
workWithStateTypeNoCompile :: forall s1.
MonadState s1 m =>
IORef (StateType s) -> StateType s1 -> m ()
at /path/to/fun-deps.hs:18:3-28
‘s’ is a rigid type variable bound by
the instance declaration
at /path/to/fun-deps.hs:17:10-78
Expected type: StateType s
Actual type: StateType s1
• In the second argument of ‘writeIORef’, namely ‘a’
In the second argument of ‘($)’, namely ‘writeIORef ref a’
In the expression: liftIO $ writeIORef ref a
• Relevant bindings include
a :: StateType s1
(bound at /path/to/fun-deps.hs:18:34)
ref :: IORef (StateType s)
(bound at /path/to/fun-deps.hs:18:30)
workWithStateTypeNoCompile :: IORef (StateType s)
-> StateType s1 -> m ()
(bound at /path/to/fun-deps.hs:18:3)
|
Compilation failed.
Я понимаю, что когда он определен в такой форме, там есть неявный forall
:
workWithStateTypeNoCompile :: forall s m a . MonadState s m => a -> StateType s -> m ()
так что технически это должно работать для каждого s
, и это будет иметь смысл при отсутствии FunctionalDependencies
, но s
известно, когда известно m
, так что часть, которую я не понимаю.
Другими словами, монада m
- это ты Он должен быть одинаковым в заголовке класса и в функции, поэтому он должен однозначно идентифицировать тип состояния s
как в заголовке экземпляра, так и в типе функции. Итак, мой вопрос: почему это не объединяется? Есть ли теоретическая причина для этого или она просто не реализована в gh c?
На самом деле, если я переписываю MonadState
в концептуально ту же функциональность, но использую TypeFamilies
вместо FunctionalDependencies
проблема кажется go прочь:
class Monad m => MonadStateFamily m where
type StateToken m :: *
class Family m a where
familyStateType :: MonadStateFamily m => a -> StateType (StateToken m) -> m ()
instance (MonadIO m, MonadStateFamily m, s ~ StateToken m) => Family m (IORef (StateType s)) where
familyStateType ref a = liftIO $ writeIORef ref a