2017-02-09 1 views
2

Gibt es eine Art "Tee" -Operation auf Option in Scala Standard-Bibliothek verfügbar? Das Beste, was ich finden konnte, ist foreach, aber sein Rückgabetyp ist Unit, daher kann es nicht verkettet werden.'Tee' Operation auf Scala Optionstyp?

Das ist, was ich suche: eine Option Instanz gegeben, führen Sie einige Operation mit Nebenwirkungen auf ihren Wert, wenn die Option nicht leer ist (Some[A]), sonst nichts tun; gib die Option auf jeden Fall zurück.

Ich habe eine benutzerdefinierte Implementierung eine implizite Klasse verwenden, aber ich frage mich, ob es ein gemeinsamer Weg ist, dies ohne implizite Konvertierung zu tun:

object OptionExtensions { 
    implicit class TeeableOption[A](value: Option[A]) { 
    def tee(action: A => Unit): Option[A] = { 
     value foreach action 
     value 
    } 
    } 
} 

Beispielcode:

import OptionExtensions._ 

val option: Option[Int] = Some(42) 
option.tee(println).foreach(println) // will print 42 twice 

val another: Option[Int] = None 
another.tee(println).foreach(println) // does nothing 

Beliebig Vorschläge?

+1

Ich nehme nicht an, dass Sie irgendetwas sehen werden, das implizit Nebenwirkungen in eine Funktion in der Standardbibliothek injiziert. –

+2

Nebenwirkungen und funktionale Programmierung sind keine Freunde. –

+0

gibt es nicht. http://stackoverflow.com/questions/9671620/how-to-keep-return-value-when-logging-in-scala http: // stackoverflow.com/questions/16742060/äquivalent-zu-rubys-tap-methode-in-scala –

Antwort

3

Um implizite Konvertierung zu vermeiden, können Sie anstelle der Methodenverkettung die Funktionszusammensetzung mit K-Kombinator verwenden. k-Kombinator gibt Ihnen eine idiomatische Möglichkeit, die Tatsache zu kommunizieren, dass Sie einen Nebeneffekt ausführen werden.

Hier ist ein kurzes Beispiel:

object KCombinator { 
    def tap[A](a: A)(action: A => Any): A = { 
    action(a) 
    a 
    } 
} 

import KCombinator._ 

val func = ((_: Option[Int]).getOrElse(0)) 
    .andThen(tap(_)(println)) 
    .andThen(_ + 3) 
    .andThen(tap(_)(println)) 

Wenn wir unsere func mit dem Argument Option(3) das Ergebnis nennen ein Int mit dem Wert von 6 und das ist sein wie die Konsole aussehen wird:

+0

Dies sollte die akzeptierte Antwort sein – eladHayun

+0

... außer dass es nicht funktioniert. :) Wenn func (None) aufgerufen wird, werden 0 und 3 in stdout gedruckt, was nicht erforderlich ist. Ich akzeptierte trotzdem die Antwort, da die Idee die richtige war und 'def tap [A] (Aktion: A => Any) (Wert: A): Option [A] = {action (value); Some (value)} 'macht den Trick:' option.flatMap (tap (println)). Foreach (println) 'druckt 42 zweimal, wenn die Option nicht leer ist. sonst tut es nichts. – proskor

+0

Es wird nur 0 und 3 gedruckt, weil ich getOrElse (0) aufgerufen habe. Einige (Wert) zurückgeben ist falsch, weil Sie mit einer verschachtelten Option von Option von ... enden werden Sie erhalten die Idee, die nicht das ist, was Sie wollen. Ich glaube nicht, dass es sehr sinnvoll ist, den Leitungshahn selbst kurzzuschließen. Sie können Mathe innerhalb der Aktion von tippen, um genau das gleiche Verhalten zu erhalten, das Sie suchen. – NetanelRabinowitz

0

Es gibt keine Möglichkeit, dies in der Standardbibliothek zu erreichen, da die Nebenwirkungen in der funktionalen Programmierung minimiert und isoliert werden. Abhängig davon, was Ihre eigentlichen Ziele sind, gibt es ein paar verschiedene Möglichkeiten, Ihre Aufgabe idiomatisch zu erledigen.

Wenn Sie eine Menge von println Befehlen ausführen, statt sie in Ihrem Algorithmus zu verteilen, würden Sie diese normalerweise in einer Sammlung sammeln und dann am Ende eine foreach println. Dies minimiert die Nebenwirkungen auf den kleinstmöglichen Einfluss. Das passt zu jedem anderen Nebeneffekt. Versuchen Sie, einen Weg zu finden, um es auf den kleinstmöglichen Raum zu drücken.

Wenn Sie versuchen, eine Reihe von "Aktionen" zu verketten, sollten Sie in futures schauen. Futures behandeln eine Aktion grundsätzlich als Wert und bieten viele nützliche Funktionen, um mit ihnen zu arbeiten.

1

Verwenden Sie einfach Karte, und machen Sie Ihre Nebenfunktionen action: A => A statt action: A => Unit entsprechen.

def tprintln[A](a: A): A = { 
    println(a) 
    a 
} 
another.map(tprintln).foreach(println)