2013-03-15 15 views
7

Ok, così ho capito come implementare Reader (e ReaderT, non mostrato) utilizzando il pacchetto operational:Come implementare Reader usando monadi gratuiti?

{-# LANGUAGE GADTs, ScopedTypeVariables #-} 

import Control.Monad.Operational 

data ReaderI r a where 
    Ask :: ReaderI r r 

type Reader r a = Program (ReaderI r) a 

ask :: Reader r r 
ask = singleton Ask 

runReader :: forall r a. Reader r a -> r -> a 
runReader = interpretWithMonad evalI 
    where evalI :: forall b. ReaderI r b -> (r -> b) 
      evalI Ask = id 

Ma io non riesco a capire per la mia vita come fare questo con monadi gratuite (Sto usando il pacchetto free di Edward Kmett). Il più vicino che ho ottenuto è questa, che capisco è barare (qualcosa su come ((->) r) è già una monade):

import Control.Monad.Free 

type Reader r a = Free ((->) r) a 

ask :: Reader r r 
ask = Free Pure 

runReader :: Reader r a -> r -> a 
runReader (Pure a) _ = a 
runReader (Free k) r = runReader (k r) r 

-- Or, more simply and tellingly: 
-- 
-- > runReader = retract 

Anche se questo non era stupido come ho il sospetto che sia, non è quello che ho voglio perché quello che voglio, in sostanza, è essere in grado di ispezionare un Reader come dati ...

+1

Non penso che si possa fare senza un tipo di funzione da qualche parte. –

risposta

3

Non credo che si possa fare tranne il modo in cui si ha. Ma, non penso che questo sia unico per il lettore. Prendere in considerazione la versione monade libera dello scrittore

data WriterF m a = WriterF m a deriving (Functor) 

type Writer m = Free (WriterF m) 

ovviamente, WriterF è isomorfo allo scrittore, ma questo si comporta il modo in cui ci si aspetterebbe con la semplice algebra

algebraWriter :: Monoid m => WriterF m (m,a) -> (m,a) 
algebraWriter (WriterF m1 (m2,a)) = (m1 <> m2,a) 

così

runWriter :: Monoid m => Writer m a -> (m,a) 
runWriter (Pure a) = (mempty,a) 
runWriter (Free x) = algebraWriter . fmap runWriter $ x 

Allo stesso modo, penso al lettore gratuito come

type ReaderF r = (->) r 

type Reader r = Free (ReaderF r) 

Mi piace questo, perché l'aggiunta di loro ti dà la monade Stato

type State x = Free ((ReaderF x) :+: (WriterF x)) 

runState :: State x a -> x -> (a,x) 
runState (Pure a) x     = (a,x) 
runState (Free (Inl f)) x    = runState (f x) x 
runState (Free (Inr (WriterF x f))) _ = runState f x 

nota, che la soluzione operativa potrebbe essere fatto per lavorare con Free utilizzando il "funtore libero", così come tutto ciò che funziona con operativa

data FreeFunctor f x = forall a. FreeFunctor (f a) (a -> x) 

ma, che è anche FreeFunctor ReaderI isomorfo a (->).

1

Bene, ho guardato questo per 3 ore e penso di aver trovato qualcosa che mi piace di più. Poiché l'applicativo Reader è lo stesso del Reader Monade, possiamo provare una versione applicativa di operational:

{-# LANGUAGE RankNTypes, GADTs, FlexibleInstances #-} 

import Control.Applicative 

data ProgramA instr a where 
    Pure :: a -> ProgramA r a 
    Ap :: ProgramA r (a -> b) -> ProgramA r a -> ProgramA r b 
    Instr :: instr a -> ProgramA instr a 

infixl `Ap` 

instance Functor (ProgramA instr) where 
    fmap f (Pure a) = Pure (f a) 
    fmap f (ff `Ap` fa) = ((f .) <$> ff) `Ap` fa 
    fmap f instr = Pure f `Ap` instr 

instance Applicative (ProgramA instr) where 
    pure = Pure 
    (<*>) = Ap 

interpretA :: Applicative f => 
       (forall a. instr a -> f a) 
      -> ProgramA instr a 
      -> f a 
interpretA evalI (Pure a) = pure a 
interpretA evalI (ff `Ap` fa) = interpretA evalI ff <*> interpretA evalI fa 
interpretA evalI (Instr i) = evalI i 

data ReaderI r a where 
    Ask :: ReaderI r r 

type Reader r a = ProgramA (ReaderI r) a 

ask :: Reader r r 
ask = Instr Ask 

runReader :: Reader r a -> r -> a 
runReader = interpretA (\Ask -> id) 

instance Monad (ProgramA (ReaderI r)) where 
    return = pure 
    ma >>= f = runReader <$> fmap f ma <*> ask 

La struttura di un ProgramA (ReaderI r) a) può essere controllato più semplicemente rispetto sia Program (ReaderI r) a o Free ((->) r) a.