2017-12-20 3 views
3

Ich frage mich, ob es eine typeclass in Katzen oder Scalaz ist, die einen Operator wie dies bietet:Scala Katzen oder Scalaz typeclass scanLeft wie

def scan[G[_],A,B](zero: B)(g: G[A],f: (A,B) => B):G[B] 

Oder wenn es existiert eine gewisse mathematische Definition für solche Betreiber (etwas wie Monad für bind/flatMap).

Die Idee von diesem typeclass wäre, eine binäre Funktion auf einen Typkonstruktor anzuwenden und den gleichen Typkonstruktor zurückzubekommen, aber mit einem anderen Typparameter (der gleiche Typ, den die Binärfunktion zurückgibt).

Ich denke, würde scanLeft der Scala Standard Library Sammlungen ähnlich sein.

+0

Lassen Sie mich fragen, was die Eigenschaften des zurückgegebenen Wertes sind - was sind die Gesetze dieser Typklasse? Ich meine, ich weiß es für "Scan" auf sammlungstypischen Typen. Aber können wir das verallgemeinern? – ziggystar

+0

@ziggystar Ja, wir könnten es verallgemeinern. Der zurückgegebene Wert ('G [_]') könnte ein Monad/Funktor sein .. der 'Scan' selbst kann in Form von 'flatMap/map' mit einem Zustand in der Methode implementiert werden. Mein Denken ähnelt Elms "foldp". Entschuldigung für die späte Antwort. – salc2

Antwort

1

Beantworten Sie die ursprüngliche Frage, nein, ich glaube nicht, dass es eine solche Klasse gibt. Sie können jedoch ähnliche Funktionen mit "Faltbar" implementieren.

Mit Katzen:

import cats.data.NonEmptyList 
import cats.Foldable 

implicit class ScanLeftable[F[_], T](val ts: F[T]) extends AnyVal { 
    def scanLeft2[U](zero: U)(f: (U, T) => U) 
        (implicit fo: Foldable[F]): NonEmptyList[U] = { 
    Foldable[F].foldLeft(ts, NonEmptyList.of(zero)) { (lu, t) => 
     f(lu.head, t) :: lu 
    }.reverse 
    } 
} 

import cats.instances.list._ 
val z = List(5, 10).scanLeft2(0)((a, b) => a + b) 
println(z == NonEmptyList.of(0, 5, 15)) //true 

Sie könnten versuchen, es allgemeinere in Bezug auf den Rückgabetyp zu machen, oder eine faule Struktur wie Iterator zurückkehren, though. Ich bin jedoch nicht sicher, wie generisch es sein könnte, ohne eine neue Typklasse einzuführen.

EDIT: Die Methode ist scanLeft2 streng, so dass ich sicher sein kann, dass die Standardbibliothek nicht aufgerufen wird.

+1

Das sieht fast so aus wie der Weg, aber es wäre interessant, eine Implementierung zu sehen, die eine 'F [U]' nicht eine 'NonEmptyList [U]' zurückgibt. Muss etwas wie das 'CanBuildFrom' aus der Standardbibliothek enthalten. –

+0

Wie @JoeK sagt, ich möchte, dass es 'F [U]' statt einer konkreten Sammlung wie 'NonEmptyList' zurückgibt, wobei' F [U] 'eine benutzerdefinierte unendliche Sammlung sein könnte, wie zum Beispiel. – salc2

3

Eine mögliche Implementierung ist zu traverse mit einem State:

import cats._, data._, implicits._ 

def scan[G[_]: Traverse: Applicative: MonoidK, A, B](list: G[A], zero: B, f: (B, A) => B): G[B] = { 
    def generate(a: A): State[B, B] = 
    for { 
     prev <- State.get[B] 
     next = f(prev, a) 
     _ <- State.set(next) 
    } yield next 

    zero.pure[G] <+> list.traverse(generate).runA(zero).value 
} 

Dies funktioniert wie scanLeft von stdlib für Vektoren und Listen (aber nicht Optionen), erfordert aber eine ganze Menge typeclasses! Leider stdlib scanLeft das erste Element vor, so dass die Ergebnissammlung immer ein Element größer als das ursprüngliche Element sein wird und keine einzige Klasse bietet eine ähnliche Operation.

Wenn es Ihnen gut geht, ohne zero voranzustellen, brauchen Sie nur G[_]Traverse und das ist nicht halb so schlimm. Wenn Sie nicht sind, ist es wahrscheinlich besser, verallgemeinernd mit Subtypen

+0

Gute Antwort, nur eine kleine Verbesserung: 'Applicative + MonoidK = Alternative' =] –