2012-12-07 8 views
14

Preferisco attenermi il più possibile al paradigma funzionale, stringendo il più vicino possibile al puramente funzionale quando il mio cervello è pronto per la sfida. Io uso F # quando possibile. Di solito, sono bloccato con VB.NET o C# (o VBA, quando sono davvero sfortunato). Quindi le mie lingue mi permettono di allontanarmi abbastanza dall'approccio funzionale.Accesso da un paradigma di programmazione funzionale

Storicamente ho ignorato la registrazione e la comunicazione con l'utente fino a quando non ho ottenuto un risultato: è sufficiente attendere l'utente. Ora sto cercando di implementare la registrazione e/o gli aggiornamenti delle barre di stato. È facile, perché le mie lingue mi consentono di scrivere sullo standard output ogni volta che voglio. Ma da un punto di vista puramente funzionale, come si fa a divulgare informazioni su ciò che accade all'interno della propria funzione al mondo esterno? La registrazione o la comunicazione con l'utente durante il calcolo è semplicemente contraria all'approccio puramente funzionale?

Sono sicuro che in Haskell si userebbe una Monade. Che dire quando usi altre lingue?

Grazie.

+0

'Preferisco restare il più vicino possibile al paradigma funzionale, stringendo il più vicino possibile come posso arrivare al puramente funzionale quando il mio cervello è pronto per la sfida, usalo quando ti dà qualche beneficio. Il codice funzionale puro spesso può aggiungere una complessità accidentale al sistema. –

+0

Probabilmente è un buon consiglio ... –

risposta

8

Diamo uno sguardo alle soluzioni monadica di Haskell. L'idea alla base del logging è che i nostri calcoli hanno un metodo aggiuntivo che scrive un messaggio da qualche parte "fuori". Ci sono molti modi come rappresentare questi calcoli, ma uno dei più generale è quello di rendere una monade:

class (Monad m) => MonadWriter w m | m -> w where 
    tell :: w -> m() 

Tipo w rappresenta i messaggi e la funzione tell è ciò che "invia" un messaggio in una monade (effetto- pieno) calcolo.

Note:

  • Haskell MonadWriter è in realtà più ricca, contiene funzioni che permettono di esaminare e modificare w, ma cerchiamo di mantenere quella parte per ora.
  • La parte | m -> w non è veramente importante per la spiegazione, significa semplicemente che w è stato risolto per un dato m.

L'implementazione più utilizzata è Writer, che in pratica è solo una coppia.Un elemento di esso è il risultato di un calcolo e l'altro elemento è una sequenza di messaggi scritti. (In realtà non è in realtà una sequenza, è più generale - un monoide, che definisce le operazioni per combinare più messaggi in uno solo.) Puoi esaminare la soluzione di Haskell guardando lo Writer module. Comunque è scritto più in generale, usando il trasformatore monad WriterT, quindi se non sei un fan di Monad, può essere piuttosto difficile da leggere. La stessa cosa può essere fatta anche in altri linguaggi funzionali, per esempio vedere this example in Scala.

Ma ci sono altre implementazioni possibili, più orientate agli effetti (ancora funzionali) della classe di tipo sopra riportata. Possiamo definire tell per emettere messaggi a qualche lavandino esterno, come sullo standard output, in un file, ecc, ad esempio:

{-# LANGUAGE FunctionalDependencies, TypeSynonymInstances, FlexibleInstances #-} 

instance MonadWriter String IO where 
    tell = putStrLn 

diciamo qui che IO può essere utilizzato come una funzione di registrazione che scrive String s in stdout . (Questo è solo un esempio semplificato, un'implementazione completa probabilmente avrebbe un trasformatore monad che aggiungerebbe la funzionalità tell a qualsiasi monade IO.)

+0

Classi e oggetti? Whaaaat? Questo non è paradigma di programmazione funzionale, è solo OOP in FP. –

+4

Non vedo il punto, le classi di tipi in Haskell sono abbastanza diverse dalle classi OO. –

+0

Grazie. Forse non vedo il punto anche io. In questa risposta, non vi è alcun caso d'uso della classe. Per favore, concentrati sul lato client dell'uso del log invece della libreria. Grazie! –

3

Sono nuovo di programmazione funzionale, ma qui è un tentativo in Scala:

object FunctionalLogging { 

    type Result = Int 

    class ResultWithLogging(val log: List[String], val result: Result) {} 

    def functionWithLogging(log: List[String], arg: String): ResultWithLogging = { 
    def function(arg: String): Result = arg.length 

    new ResultWithLogging(log :+ ("Calling function(" + arg +")"), function(arg)) 
    } 

    val result = functionWithLogging(List(), "Hello world!") 

    // -- Pure functional code ends here -- 
    println("Result = " + result.result) 
    println("Log = " + result.log) 
} 

E 'funzionale, in quanto non ci sono effetti collaterali, ma ovviamente il registro fa parte degli argomenti di funzione e ritorno, in modo non è molto elegante o pratico.

Mi sembra che il logging sia un effetto collaterale desiderabile per definizione, quindi se si va con la mia definizione la domanda è come isolare il non funzionale dal codice funzionale. In pratica, probabilmente inizierei con un oggetto Scala (probabilmente troppo simile a un singleton - un tratto probabilmente è migliore di Scala), o un attore che accumula i messaggi di registrazione e fa tutto ciò che deve essere fatto con loro.

Questa è una visione più pragmatica: Logging in Scala

Modifica

questa domanda parla di Haskell Monadi e IO: What other ways can state be handled in a pure functional language besides with Monads?

+0

Mi piace il tuo processo mentale qui. Nel mio caso, però, la funzione era piuttosto long-running, e volevo segnalare come elaborato, piuttosto che aspettare che il suo ritorno riporti il ​​suo log. Tuttavia, se nessun altro risponde alla mia domanda, probabilmente la segnerò come risposta, perché, mentre la risposta di Haskell è una grande, la mia domanda riguardava più specificamente gli approcci non Monad. Grazie! –