Почему Djinn не может реализовать общие монадические функции?
Недавно я наткнулся на Djinn и кратко играл с ним, чтобы попытаться выяснить, будет ли это полезно в моем повседневном рабочем процессе кодирования. Я был рад видеть, что у Джинна были монады, и он попытался понять, сможет ли он найти какие-нибудь классные функции.
Джинн действительно натворил чудеса. Типичная подпись изначально (по крайней мере для меня) неинтуитивной функции >>= (>>=)
равна Monad m => ((a -> m b) -> m a) -> (a -> m b) -> m b
. Джинн смог немедленно демистифицировать это, указав
Djinn> f ? Monad m => ((a -> m b) -> m a) -> (a -> m b) -> m b
f :: (Monad m) => ((a -> m b) -> m a) -> (a -> m b) -> m b
f a b = a b >>= b
К сожалению, Djinn, похоже, не может найти другие стандартные функции на монадах, несмотря на то, что знает о стандартном классе Monad.
-
join
(который должен быть join = (>>= id)
или в Djinn более подробный синтаксис join a = a >>= (\x -> x)
)
Djinn> join ? Monad m => m (m a) -> m a
-- join cannot be realized.
-
liftM
(который должен быть liftM f = (>>= (return . f))
или в Djinn более подробный синтаксис liftM a b = b >>= (\x -> return (a x))
)
Djinn> liftM ? Monad m => (a -> b) -> m a -> m b
-- liftM cannot be realized.
-
Даже базовый return :: Monad m => m a -> m (m a)
не может быть найден Djinn или return :: Monad m => (a, b) -> m (a, b)
.
Djinn> f ? Monad m => (a, b) -> m (a, b)
-- f cannot be realized.
Джинн знает, как использовать \
для построения анонимных функций, так почему это так?
Мое грубое подозрение состоит в том, что, возможно, у Джинна есть упрощенное понятие typeclass и как-то рассматривает m a
как "фиксированный", так что m (a, b)
не рассматривается как случай m a
, но я понятия не имею, как сделать что более конкретна, чем его текущая рука-волновая форма или эта интуиция верна.
Ответы
Ответ 1
Поддерживающие классы классов выглядят очень похоже на поддержку классов ранга 2; Для сравнения:
join :: Monad m
=> m (m a) -> m a
против
join :: (forall a. a -> m a)
-> (forall a b. m a -> (a -> m b) -> m b)
-> m (m a) -> m a
К сожалению, методы, используемые Djinn, вообще не обрабатывают типы ранга 2. Если вы плаваете на форелях так, чтобы Djinn мог обработать их, то вдруг вы получите конкретные варианты:
join :: (b -> m b)
-> (m c -> (c -> m d) -> m d)
-> m (m a) -> m a
который выглядит намного меньше, чем вы могли его реализовать! Если вы скажете Djinn, какие экземпляры использовать, конечно, это намного лучше.
join :: (b -> m b)
-> (m (m a) -> (m a -> m a) -> m a)
-> m (m a) -> m a
Для этого Djinn даст правильную реализацию.... но тогда это обман.