2011-08-28 10 views
26

Acabo de reinventar alguna mónada, pero no estoy seguro de cuál. Le permite modelar los pasos de un cálculo, por lo que puede intercalar los pasos de numerosos cálculos para encontrar cuál termina primero.Haskell: ¿Qué mónada acabo de reinventar?

{-# LANGUAGE ExistentialQuantification #-} 
module Computation where 

-- model the steps of a computation 
data Computation a = forall b. Step b (b -> Computation a) | Done a 

instance Monad Computation where 
    (Step b g) >>= f = Step b $ (>>=f) . g 
    (Done b) >>= f = Step b f 
    return = Done 

runComputation :: Computation a -> a 
runComputation (Step b g) = runComputation (g b) 
runComputation (Done a) = a 

isDone :: Computation a -> Bool 
isDone (Done _) = True 
isDone _ = False 

-- an order for a set of computations 
data Schedule a = a :> Computation (Schedule a) | Last 

toList :: Schedule a -> [a] 
toList Last = [] 
toList (a :> c) = a : (toList . runComputation) c 

-- given a set of computations, find a schedule to generate all their results 
type Strategy a = [Computation a] -> Computation (Schedule a) 

-- schedule all the completed computations, and step the rest, 
-- passing the remaining to the given function 
scheduleOrStep :: (Queue (Computation a) -> Computation (Schedule a)) -> Strategy a 
scheduleOrStep s cs = scheduleOrStep' id cs 
    where scheduleOrStep' q ((Done a):cs) = Done $ a :> scheduleOrStep' q cs 
     scheduleOrStep' q ((Step b g):cs) = scheduleOrStep' (q . (g b:)) cs 
     scheduleOrStep' q [] = s q 

-- schedule all completed compuations, step all the rest once, and repeat 
-- (may never complete for infinite lists) 
-- checking each row of 
-- [ [ c0s0, c1s0, c2s0, ... ] 
-- , [ c0s1, c1s1, c2s1, ... ] 
-- , [ c0s2, c1s2, c2s2, ... ] 
-- ... 
-- ] 
-- (where cNsM is computation N stepped M times) 
fair :: Strategy a 
fair [] = Done Last 
fair cs = scheduleOrStep (fair . ($[])) cs 

-- schedule more steps for earlier computations rather than later computations 
-- (works on infinite lists) 
-- checking the sw-ne diagonals of 
-- [ [ c0s0, c1s0, c2s0, ... ] 
-- , [ c0s1, c1s1, c2s1, ... ] 
-- , [ c0s2, c1s2, c2s2, ... ] 
-- ... 
-- ] 
-- (where cNsM is computation N stepped M times) 
diag :: Enqueue (Computation a)-> Strategy a 
diag _ [] = Done Last 
diag enq cs = diag' cs id 
    where diag' (c:cs) q = scheduleOrStep (diag' cs) (enq c q $ []) 
     diag' [] q = fair (q []) 

-- diagonal downwards : 
-- [ c0s0, 
-- c1s0, c0s1, 
-- c2s0, c1s1, c0s2, 
-- ... 
-- cNs0, c{N-1}s1, ..., c1s{N-1}, c0sN, 
-- ... 
-- ] 
diagd :: Strategy a 
diagd = diag prepend 

-- diagonal upwards : 
-- [ c0s0, 
-- c0s1, c1s0, 
-- c0s2, c1s1, c2s0, 
-- ... 
-- c0sN, c1s{N-1}, ..., c{s1N-1}, cNs0, 
-- ... 
-- ] 
diagu :: Strategy a 
diagu = diag append 

-- a queue type 
type Queue a = [a] -> [a] 
type Enqueue a = a -> Queue a -> Queue a 

append :: Enqueue a 
append x q = q . (x:) 

prepend :: Enqueue a 
prepend x q = (x:) . q 

Creo que esto es probablemente una especie de mónada de enhebrar?

+18

Haskell es el único idioma que conozco donde no se puede saber qué rueda se acaba de reinventar ... –

+0

Estaba a punto de cerrar como "demasiado localizado", pero la gente realmente pasa su tiempo sabiendo que están reinventando cosas en Haskell pero no ¿qué? ¿Están reinventando, haciendo que esta pregunta sea legítima (suponiendo que mucha gente termine reinventando esto exactamente, sea lo que sea)? – Mat

+11

@Mat: Sí, en realidad. Al menos de ciertas maneras. La gente ocasionalmente no hace bromas que en Haskell, con un código suficientemente genérico, si el tipo revisa es casi seguro que esté haciendo algo útil, incluso si no está seguro de qué. Esto es más o menos en la misma línea, en el sentido de que si inventas algo para resolver un problema específico y se generaliza claramente, es probable que ya se haya hecho. Cuando estaba aprendiendo Haskell por primera vez, al menos una vez generalicé una solución específica solo para darme cuenta de que había reinventado un rincón oscuro de las bibliotecas estándar. –

Respuesta

5

Parece una mónada de reanudación con estado. Creo que solía haber una mónada de reanudación en MTL alrededor de GHC 6.6, pero si había desapareció. William Harrison de la Universidad de Missouri tiene una serie de documentos sobre la reanudación de las mónadas: http://people.cs.missouri.edu/~harrisonwl/publications.html

2

No he dedicado demasiado tiempo a comprender su código, pero realmente suena como la mónada de coronas del paquete de mónadas-coronas, que puede ser un poco más general.

+0

Eché un breve vistazo a eso: parecía que las corotinas se basaban en la comunicación, mientras que éstas no son comunicativas. – rampion

2

Esto se parece a la definición de fusión de secuencias usada por Don Stewart hace un tiempo, y también algo relacionado con iterar (aunque sin la idea de insertar datos en iteratee usando un enumerador), pero menos que la fusión de secuencias Supongo.

5

No entiendo por qué no

data Computation a = Step (Computation a) | Done a 

instance Monad Computation where 
    (Step g) >>= f = Step $ g >>= f 
    (Done b) >>= f = Step (f b) 
    return = Done 

No estoy seguro de lo que esta mónada es, pero definitivamente es más simple y parece ser equivalente en muchos aspectos.

+0

buen punto. Me gusta. – rampion

+1

Esa es la mónada de reanudación simple. La mónada original de Rampion tiene un estado enhebrado como la función 'unfoldr', aunque realmente no puedo ver si el estado es necesario para el ejemplo dado. En uno de sus documentos, William Harrison comenta que la mónada de reanudación es básicamente "inerte" sin agregarle estado (inerte no es su palabra original, pero no puedo encontrar la cita en este momento). –

+1

@stephen tetley: con la cuantificación existencial que he dado al estado, sin embargo, no hay nada que * se pueda * hacer con él, por lo que, en efecto, solo está demorando la computación. Lo cual es lo que la evaluación perezosa hace de todos modos, por lo que es equivalente a la mónada de reanudación. Entonces esa es la respuesta. – rampion