2016-07-02 24 views
8

W programie Scalaz można odwzorować funkcję na inną funkcję. Kiedy chciałbym użyć map przez andThen? Czy istnieje wyraźna przewaga przy użyciu map? DziękiGdy mapa nad funkcją jest przydatna, kiedy już ją posiadasz, a następnie

Na przykład

val f: Int => Int = (a) => a + 10 

val g: Int => Int = (a) => a * 100 

(f map g map {_*3})(10) == (f andThen g andThen {_*3})(10) // true 
+0

Studnia 'map' działa na dowolnym funktorze, a nie tylko na funkcjach. – Bergi

+0

Myślałem o tym też. Mogę więc mapować listę opcji i listę funktora, ale nie potrafię wymyślić, jak utworzyć listę funktorów. – thlim

Odpowiedz

12

Pomijając szczegóły implementacji przez chwilę, mapjestandThen dla funkcji (pod instancji funktora dla A => ?) i tak naprawdę nie zrobić wiele sensu mówić o preferowaniu jednej z drugiej, jeśli mówimy o funkcjach, a nie o wyższym poziomie abstrakcji.

Jakie metody, takie jak map (i klasy typu, takie jak Functor bardziej ogólnie) pozwalają nam to zrobić, są abstrakcyjne dla określonych typów lub typów konstruktorów. Załóżmy, że chcemy napisać metodę incrementResult, która działa na przykład na A => Int lub Kleisli[Option, A, Int]. Typy te nie mają nic wspólnego, jeśli chodzi o dziedziczenie (krótkie z AnyRef, który jest bezużyteczny), ale A => ? i Kleisli[Option, A, ?] są oba funktory, więc możemy napisać:

import scalaz._, Scalaz._ 

def incrementResult[F[_]: Functor](f: F[Int]): F[Int] = f.map(_ + 1) 

a następnie używać go w ten sposób (zauważ, że używam kind-projector uproszczenie składni typ a bit):

scala> val plainOldFuncTriple: Int => Int = _ * 3 
plainOldFuncTriple: Int => Int = <function1> 

scala> val optionKleisliTriple: Kleisli[Option, Int, Int] = Kleisli(i => Some(i * 3)) 
optionKleisliTriple: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>) 

scala> val f = incrementResult[Int => ?](plainOldFuncTriple) 
f: Int => Int = <function1> 

scala> val k = incrementResult[Kleisli[Option, Int, ?]](optionKleisliTriple) 
k: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>) 

scala> f(10) 
res0: Int = 31 

scala> k(10) 
res1: Option[Int] = Some(31) 

W tym przypadku konkretnie istnieją lepsze sposoby wdrożenia tej operacji, ale to pokazuje ogólną ideę, nie mogliśmy napisać metoda pojedyncza, która działa dla zwykłych funkcji i strzałek Kleisli przy użyciu andThen, ale możemy z dodatkowym poziomem abstrakcji, który daje nam map.

Tak, aby odpowiedzieć na Twoje pytanie-you'd korzystanie map jeśli chcesz streszczenie na wszystkich konstruktorów typu, które mają wystąpienie funktora, ale jeśli pracujesz konkretnie z funkcji, mapjestandThen, i-jak długo ponieważ wciąż odkładamy szczegóły implementacji - nie ma znaczenia, który z nich wybierzesz.


Przypis: the map że syntax pakiet Scalaz daje ci dla wartości typów, które instancje funktora jest zaimplementowany jako metodę rozszerzenia, więc nie ma odrobinę napowietrznych (zarówno w czasie kompilacji i wykonywania) zaangażowana w użyciu map zamiast andThen dla funkcji. Jeśli pracujesz tylko z funkcjami i nie potrzebujesz dodatkowej abstrakcji, równie dobrze możesz przejść z andThen.

+1

Możemy * napisać jedną metodę, która działa zarówno przy użyciu 'jak i Then': 'def incrementResult [~> [_, _]: Compose, A] (f: A ~> Int): A ~> Int = f and Then (_ + 1) ' –

+1

@ JulienRichard-Foy Sure (to właśnie miałem na myśli przez" lepsze sposoby realizacji tej operacji), ale to jest rodzaj rozproszenia, ponieważ to "i To" nie jest 'i To na' Funkcja1', które o to pyta OP. –

+0

@ JulienRichard-Foy Jak mogę to naprawić? wartość iNie jest członkiem typu parametr ~> [A, Int] ' – thlim