2011-11-22 11 views
16

He decidido que hoy es el día en que corrijo algunas de mis funciones puras que se ejecutan innecesariamente en una acción monádica. Esto es lo que tengo.¿Cómo organizo mis funciones puras con mis acciones monádicas idiomáticamente?

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays dayList = 
    flagWeekEnds dayList >>= 
    flagHolidays >>= 
    flagScheduled >>= 
    flagASAP >>= 
    toWorkDays 

Aquí está flagWeekEnds, a partir de ahora.

flagWeekEnds :: [C.Day] -> Handler [(C.Day,Availability)] 
flagWeekEnds dayList = do 
    let yepNope = Prelude.map isWorkDay dayList 
     availability = Prelude.map flagAvailability yepNope 
    return $ Prelude.zip dayList availability 

flagHolidays sigue un patrón similar. toWorkDays simplemente cambia un tipo a otro, y es una función pura.

flagScheduled y flagASAP son acciones monádicas. No estoy seguro de cómo combinar las acciones monádicas con las funciones puras idiomáticamente en flagWorkDays. ¿Podría alguien ayudarme a reparar flagWorkDays, suponiendo que flagWeekEnds y flagHolidays se han hecho puros?

Respuesta

28

Demos un paso atrás por un momento. Tiene dos tipos de funciones, algunas puras con tipos del formulario a -> b, y algunas monádicas del tipo a -> m b.

Para evitar confusiones, sigamos con la composición de derecha a izquierda. Si prefiere leer de izquierda a derecha, simplemente invierta el orden de las funciones y reemplace (<=<) con (>=>) y (.) con (>>>) desde Control.Arrow.

Existen cuatro posibilidades para cómo se pueden componer.

  1. Pure then pure. Utilice la composición de función normal (.).

    g :: a -> b 
    f :: b -> c 
    f . g :: a -> c 
    
  2. puro continuación monádico. También use (.).

    g :: a -> b 
    f :: b -> m c 
    f . g :: a -> m c 
    
  3. Monádica continuación monádico. Use la composición de kleisli (<=<).

    g :: a -> m b 
    f :: b -> m c 
    f <=< g :: a -> m c 
    
  4. Monádica entonces pura. Use fmap en la función pura y (.) para componer.

    g :: a -> m b 
    f :: b -> c 
    fmap f . g :: a -> m c 
    

Haciendo caso omiso de los detalles de los tipos involucrados, sus funciones son:

flagWeekEnds :: a -> b 
flagHolidays :: b -> c 
flagScheduled :: c -> m d 
flagASAP :: d -> m e 
toWorkDays :: e -> f 

Vamos desde la parte superior. flagWeekEnds y flagHolidays son ambos puros. Caso 1.

flagHolidays . flagWeekEnds 
    :: a -> c 

Esto es puro. El siguiente es flagScheduled, que es monádico. Caso 2.

flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m d 

siguiente es flagASAP, ahora tenemos dos funciones monádicos. Caso 3.

flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m e 

Y, por último, tenemos la función pura toWorkDays. Caso 4.

fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m f 

Y listo.

+2

+1 para una explicación general – fuz

+3

Brillante, como siempre. – Ingo

5

No es muy difícil. Básicamente, simplemente reemplace (>>=) por (.) y cambie el orden de los operandos. do sintaxis puede ayudar a aclarar. También hice el ejemplo sin puntos usando el combinador Kleisli (pez) (<=<) :: (b -> m c) -> (a -> m b) -> a -> m c, que es esencialmente (.) para mónadas.

import Control.Monad 

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays = 
    fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
+0

¿Qué pasa con 'dayList'? – Tarrasch

+0

@Tarrasch sin sentido. Olvidé eliminar el parámetro adicional – fuz

+0

¿Existe un nombre/función predefinida para 'flip (.)'? Estoy un poco triste porque la transformación aquí tuvo que poner las cosas atrás – hugomg

5

Para rellenar la respuesta de FUZxxl, vamos a pureify flagWeekEnds:

flagWeekEnds :: [C.Day] -> [(C.Day,Availability)] 
flagWeekEnds days = days `zip` map (flagAvailability . isWorkDay) days 

A menudo se ponga una "s" después de los nombres de variables (day ->days) cuando su lista (como lo hace con plural en inglés).

Cuestiones relacionadas