Можно ли реализовать `(Аппликативный m) => Аппликативный (StateT s m)`?

В настоящее время я работаю над Data.Fresh и Control.Monad.Trans.Fresh, которые соответственно. определить интерфейс для генерации свежих переменных и трансформатор монады, который реализует этот интерфейс.

Первоначально я думал, что возможно реализовать экземпляр Applicative для моего FreshT v m с единственным требованием, что Applicative m существует. Тем не менее, я застрял, и мне показалось, что мне нужно Monad m. Не доверяя моему Haskell-fu, я затем повернулся к пакету трансформаторов и был удивлен тем, что нашел в Control.Monad.Trans.State.Lazy и .Strict:

instance (Functor m, Monad m) => Applicative (StateT s m) where
    pure = return
    (<*>) = ap

Итак, вот мой вопрос: возможно ли создать экземпляр с эквивалентной семантикой со следующей головой экземпляра?

instance (Applicative m) => Applicative (StateT s m) where

Ответы

Ответ 1

Учтите, что у вас есть две функции:

 f :: s -> m (s, a -> b)
 g :: s -> m (s, a)

И вы хотите создать функцию h = StateT f <*> StateF g

 h :: s -> m (s, b)

Из вышесказанного вы можете s перейти на f, чтобы у вас было:

 f' :: m (s, a -> b)
 g :: s -> m (s, a)

Однако чтобы получить s из f', вам понадобится Monad (что бы вы ни делали с аппликативным, оно все равно будет в форме m s, поэтому вы не сможете применить значение к g).

Вы можете играть с определениями и использовать бесплатную монаду, но для краха состояния вам нужно join.

Ответ 2

Хотя, как отмечено в предыдущем ответе, этот экземпляр не может быть определен вообще, стоит отметить, что, когда f является Applicative и s является Monoid, StateT s f также Applicative, так как его можно рассматривать как композицию аппликативных функторов:

StateT s f = Reader s `Compose` f `Compose` Writer s

Ответ 3

Более слабый вариант трансформатора Applicative

Хотя для StateT невозможно определить аппликативный трансформатор, возможно определить более слабый вариант, который работает. Вместо того, чтобы иметь s -> m (a, s), где состояние решает следующий эффект (поэтому m должна быть монадой), мы можем использовать m (s -> (a, s)) или эквивалентно m (State s a).

import Control.Applicative
import Control.Monad
import Control.Monad.State
import Control.Monad.Trans

newtype StateTA s m a = StateTA (m (State s a))

Это строго слабее, чем StateT. Каждый StateTA можно сделать в StateT (но не наоборот):

toStateTA :: Applicative m => StateTA s m a -> StateT s m a
toStateTA (StateTA k) = StateT $ \s -> flip runState s <$> k

Определение Functor и Applicative - это только вопрос о выполнении операций State в базовом m:

instance (Functor m) => Functor (StateTA s m) where
    fmap f (StateTA k) = StateTA $ liftM f <$> k
instance (Applicative m) => Applicative (StateTA s m) where
    pure = StateTA . pure . return
    (StateTA f) <*> (StateTA k) = StateTA $ ap <$> f <*> k    

И мы можем определить аппликативный вариант lift:

lift :: (Applicative m) => m a -> StateTA s m a
lift = StateTA . fmap return

Обновление: На самом деле это не нужно, так как состав двух аппликативных функторов всегда является аппликативным функтором (в отличие от монад). Наш StateTA изоморфен Compose m (State s), который автоматически Applicative:

instance (Applicative f, Applicative g) => Applicative (Compose f g) where
    pure x = Compose (pure (pure x))
    Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)

Поэтому мы могли бы написать просто

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Applicative
import Control.Monad.State
import Data.Functor.Compose

newtype StateTA s m a = StateTA (Compose m (State s) a)
    deriving (Functor, Applicative)