haskell monad-transformers state-monad

haskell - ¿Es posible implementar `(Applicative m)=> Applicative(StateT sm)`?



monad-transformers state-monad (3)

Variante más débil de un transformador Applicative

Aunque no es posible definir un transformador aplicativo para StateT , es posible definir una variante más débil que funcione. En lugar de tener s -> m (a, s) , donde el estado decide el siguiente efecto (por lo tanto, m debe ser una mónada), podemos usar m (s -> (a, s)) , o equivalentemente m (State sa) .

import Control.Applicative import Control.Monad import Control.Monad.State import Control.Monad.Trans newtype StateTA s m a = StateTA (m (State s a))

Esto es estrictamente más débil que StateT . Cada StateTA se puede convertir en StateT (pero no al revés):

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

Definir el Functor y el Applicative es solo una cuestión de elevar las operaciones del State a la m subyacente:

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

Y podemos definir una variante de lift aplicativa:

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

Actualización: en realidad, lo anterior no es necesario, ya que la composición de dos funtores aplicativos es siempre un funtor aplicativo (a diferencia de las mónadas). Nuestro StateTA es isomorfo para StateTA Compose m (State s) , que es automáticamente Applicative :

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

Por lo tanto podríamos escribir solo

{-# 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)

Actualmente estoy trabajando en Data.Fresh y Control.Monad.Trans.Fresh , que resp. define una interfaz para generar nuevas variables y un transformador de mónada que implementa esta interfaz.

Inicialmente pensé que sería posible implementar la instancia de Applicative para mi FreshT vm con el único requisito de que exista Applicative m . Sin embargo, me quedé atascado y parecía que necesitaba requerir Monad m . Sin confiar en mi Haskell-fu, me volví hacia el paquete de transformadores y me sorprendió lo que encontré en Control.Monad.Trans.State.Lazy y .Strict :

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

Así que aquí está mi pregunta: ¿es posible crear una instancia con semántica equivalente con el siguiente encabezado de instancia?

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


Aunque, como se señaló en la respuesta anterior, esta instancia no se puede definir en general, vale la pena señalar que, cuando f es Applicative StateT sf es un StateT sf , StateT sf también es Applicative , ya que puede considerarse como una composición de funtores aplicativos. :

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


Considera que tienes dos funciones:

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

Y desea crear una función h = StateT f <*> StateF g

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

De lo anterior tiene una s que puede pasar a f por lo que tiene:

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

Sin embargo, para obtener s fuera de f'' necesitas la mónada (lo que harías con el aplicativo todavía estaría en forma de ms por lo que no podrías aplicar el valor a g ).

Puedes jugar con las definiciones y usar la mónada gratuita, pero para el colapso del estado al que necesitas join .