2016-02-01 34 views
8

Sto cercando di imparare il pacchetto pipes scrivendo la mia funzione sum e sto diventando perplesso. Mi piacerebbe non utilizzare le funzioni di utilità da Pipes.Prelude (poiché ha sum e fold e altre funzioni che lo rendono banale) e utilizzare solo le informazioni come descritto in Pipes.Tutorial. Il tutorial non parla dei costruttori di Proxy, ma se guardo nell'origine di sum e fold utilizza quei costruttori e mi chiedo se sia possibile scrivere la mia funzione sum senza conoscere questi dettagli di basso livello.Come scrivere una funzione "somma" di Haskell Pipes?

Ho problemi a venire a patti con come questa funzione sarebbe in grado di continuare a prendere valori fino a quando ci sono valori disponibili, e quindi in qualche modo restituire tale somma all'utente. Credo che il tipo sarebbe:

sum' :: Monad m => Consumer Int m Int 

Mi sembra questo potrebbe funzionare, perché questa funzione potrebbe consumare valori fino a quando non ci sono più, per poi tornare la somma finale. Vorrei usare in questo modo:

mysum <- runEffect $ inputs >-> sum' 

Tuttavia, la funzione in Pipes.Prelude ha la seguente firma invece:

sum :: (Monad m, Num a) => Producer a m() -> m a 

quindi credo che questo è il mio primo ostacolo. Perché la funzione sum accetta un Producer come argomento anziché utilizzare >-> per connettersi?


FYI ho finito con il seguente dopo la risposta da danidiaz:

sum' = go 0 
    where 
    go n p = next p >>= \x -> case x of 
     Left _  -> return n 
     Right (_, p') -> go (n + 1) p' 
+0

bene il 'sum' nei tubi prende qualcosa che produce * * numeri e li riassume in un'azione monadica ... l'idea è abbastanza simile ai tuoi se ci pensi - anche il pacchetto è progettato in modo che non ti interessi i dettagli ma usi 'fold' e il resto delle primitive fornite;) – Carsten

risposta

5

Consumers sono in realtà molto limitati in ciò che possono fare. Non sono in grado di rilevare la fine dell'input (pipes-parse utilizza una tecnica diversa per questo) e quando un'altra parte della pipeline si arresta (ad esempio lo Producer a monte) quella parte che deve fornire il valore del risultato per la parte pipeline. Quindi, inserire la somma nel valore di ritorno di Consumer non funzionerà in generale.

Alcune alternative sono:

  • implementare una funzione che tratta direttamente con Producer interni, o forse utilizza una funzione ausiliaria come next. Esistono adattatori di questo tipo che possono alimentare i dati Producer per utenti "più intelligenti", ad esempio Fold s dal pacchetto foldl.

  • continuare ad usare un Consumer, ma invece di mettere la somma del valore di ritorno della Consumer, utilizzare un WriterT come monade base con un Sum Int monoide come accumulatore. In questo modo, anche se il Producer si interrompe per primo, è comunque possibile eseguire il writer per arrivare all'accumulatore. Questa soluzione è probabilmente meno efficiente.

codice di esempio per l'approccio WriterT:

import Data.Monoid 
import Control.Monad 
import Control.Monad.Trans.Writer 
import Pipes 

producer :: Monad m => Producer Int m() 
producer = mapM_ yield [1..10] 

summator :: Monad n => Consumer Int (WriterT (Sum Int) n)() 
summator = forever $ await >>= lift . tell . Sum 

main :: IO() 
main = do 
    Sum r <- execWriterT . runEffect $ producer >-> summator 
    print r 
+0

" Impossibile rilevare la fine dell'input "era cosa Mi mancava Grazie mille. – Ana

Problemi correlati