Почему 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 даст правильную реализацию.... но тогда это обман.