/ / flatMap (func) versus flatMap (func (_)) - scala

flatMap (func) versus flatMap (func (_)) - scala

Bardzo mnie zaskoczyło, że nie znalazłem już dla niego pytania, dlaczego:

val p: Int => Option[Int] = Some(_)
List(1, 2, 3).flatMap(p)

Otrzymuję:

<console>:14: error: type mismatch;
found   : Int => Option[Int]
required: Int => scala.collection.GenTraversableOnce[?]
List(1, 2, 3).flatMap(p)

Ale jeśli zastąpię ostatnią linię tym, to kompiluje i działa zgodnie z oczekiwaniami:

List(1, 2, 3).flatMap(p(_))

Moje podejście do problemu polega na tym, że w przypadku p(_) system wnioskowania typu uruchamia się, aby zdecydować o typie lambda i sposobie, w jaki znajduje odpowiednią konwersję niejawną Option[Int] (option2Iterable, Wierzę). Właśnie p, typ jest już znany i jest niepoprawny, więc nie jest podejmowana żadna konwersja (i tam nie ma konwersji) Function1 powracający Option do Function1 powracający GenTraversableOnce).

Czy to rozumowanie jest właściwe? A jeśli tak, czy jest jakiś powód, dla którego nie powinienem zgłosić tego jako błędu / problemu?

EDYCJA: nowy zwrot: widziałem p.apply wspomniano w pewnym (niestety) usuniętym komentarzu (choć dotyczyło to stylu kodowania). Zaskakująco, działa równie dobrze jak p(_) robi.

Odpowiedzi:

8 dla odpowiedzi № 1

Kiedy piszesz List(1, 2, 3).flatMap(p(_)) to, co dzieje się za kulisami, to ta funkcja p odradza się i zawija w inną funkcję, która częściowo go stosuje - co oznacza, że ​​wszystkie niezbędne niejawne konwersje, jeśli takie istnieją, zostaną również zastosowane w ciele tej nowej funkcji.

Kiedy piszesz List(1, 2, 3).flatMap(p), nie ma aplikacji funkcji i próbujesz przekazać Int => Option[Int] co jest niezgodne z podpisem Int => GenTraversableOnce[Int]i chociaż zakres zawiera niejawną konwersję z Option[T] do Iterable[T], nie ma konwersji od Function1[Int, Option[Int]] do Function1[Int, Iterable[Int]] określone.

Przyczyną tego prawdopodobnie jest to, że funkcje arbitralnej arii mają praktycznie nieskończoną liczbę odmian ze względu na rodzajowe, a ponieważ Functions nie dzielą się supertraitem, co wymagałoby sporej ilości implikacji dla każdego typu funkcji.


Oto konstrukcja, która się rozszerza flatMap wystarczy, aby osiągnąć pożądany wynik p. Jednakże czyni to już mało znanym sygnaturą flatMap jeszcze mniej jasne (o wiele mniej jasne). Wydaje mi się, że nie ma żadnej przeszkody technicznej do wdrożenia tego zachowania, ale złożoność podpisów jest powodem, dla którego często przywoływana jest biblioteka scala-collections.

import scala.collection.GenTraversableOnce
import scala.collection.generic.CanBuildFrom

implicit class ListEx[A](list: List[A]) {
def flatMap2[B, M[_], That](f: A => M[B])
(implicit bf: CanBuildFrom[List[A], B, That],
view: M[B] => GenTraversableOnce[B]): That =
list.flatMap(f andThen view)
}

val p: Int => Option[Int] = Some(_)

List(1, 2, 3) flatMap2 p

4 dla odpowiedzi nr 2
List(1, 2, 3).flatMap(p(_))

jest skompilowany do:

List(1,2,3).flatMap(x => p(x))

I jako p(x) wraca Option[Int] i flatMap wymagania GenTraversableOnce[Int] więc scala.Option.option2Iterable jest stosowany.

Opcja nie dziedziczy po GenTraversableOnce. Aby ta składnia działała:

List(1,2,3).flatMap(p)

potrzebujesz niejawnej konwersji z Int => Option[Int] do Int => GenTraversableOnce[Int], coś w tym stylu:

import scala.collection.GenTraversableOnce

implicit def conv(c: Int => Option[Int]): Int => GenTraversableOnce[Int] = {
a => Option.option2Iterable(c(a))
}
val p: Int => Option[Int] = Some(_)
List(1, 2, 3).flatMap(p)

Dla mnie to nie jest błąd, ale zgadzam się, to też nie jest intuicyjne.