2011-09-02 5 views
14

Jeśli tworzę dla zrozumienia z definicji wartości z opcją, że działa zgodnie z oczekiwaniami:Dlaczego Scala wybrać typ „produkt” za „dla” wyrażeń związanych albo i definicji wartości

scala> for (a <- Some(4); b <- Some(5); val p = a * b) yield p 
res0: Option[Int] = Some(20) 

robi to samo rzecz albo działa, jeśli nie mam definicji wartość:

scala> for (a <- Right(4).right; b <- Right(5).right) yield a * b 
res1: Either[Nothing,Int] = Right(20) 

Ale jeśli kiedyś definicję wartości, scala wydaje się wywnioskować niewłaściwy typ kontener dla dla zrozumienia:

scala> for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
<console>:8: error: value map is not a member of Product with Serializable with Either[Nothing,(Int, Int)] 
for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
          ^

Dlaczego to robi? Jakie sposoby obejścia tego zachowania są dostępne?

Odpowiedz

19

Problemy pochodzi z val p = a*b Jeśli piszesz prostszej

dla (a < - prawo (4) .right; b < - Prawo (5) .right) uzyskując a * b

go kompiluje i dostajesz właściwy wynik.

Twój problem ma dwie przyczyny

pierwsze, Either występy map i flatMap nie mają zwykły podpis, a mianowicie do procedury map i flatMap zdefiniowane w ogólnej klasy M[A], (A => B) => M[B] i (A => M[B]) => M[B]. Procedura M[A] jest zdefiniowana jako Either[A,B].RightProjection, ale w wynikach i argumentach mamy Either[A,B], a nie rzut.

Po drugie, przetłumaczono sposób, w jaki val p = a*b w celu zrozumienia. Scala odniesienia, 6,19 s 90:

generator P < - E a następnie wartości definicji p "e" jest przeliczeniu na poniższej generatora pary wartości, w którym X i 'są świeże nazwy :

(p,p′) <- for([email protected]<-e) yield {val x′@p′ = e′; (x,x′)} 

Załóżmy uprościć kod tylko trochę, upuszczając a <-. Ponadto, b i zmieniono nazwę na p i pp na bliżej reguły przepisywania, z pp dla. a powinna być w zakresie dla (p < - prawy (5) .right; val s = A * p) dostarcza PP

zgodnie z reguły trzeba wymienić generator + definicji. Co się z tym wiąże, for( i )yield pp, bez zmian.

for((p, pp) <- for([email protected] <- Right(5).right) yield{val [email protected] = a*p; (x,xx)}) yield pp 

Wewnętrzna dla jest przepisany do prostego mapie

for((p, pp) <- Right(5).right.map{case [email protected] => val [email protected] = a*p; (x,xx)}) yield pp 

Tutaj jest problem. Right(5).right.map(...) jest typu Either[Nothing, (Int,Int)], a nie Either.RightProjection[Nothing, (Int,Int)], jak byśmy chcieli. Nie działa na zewnątrz dla (który przekształca się w map).Nie ma metody map na Either, jest ona zdefiniowana tylko na rzutach.

Jeśli przyjrzeć się dokładnie komunikatowi o błędzie, mówi tak, nawet jeśli wspomina o Product i Serializable, mówi, że jest to Either[Nothing, (Int, Int)] i że nie ma na nim zdefiniowanej mapy. Para (Int, Int) pochodzi bezpośrednio z reguły przepisywania.

Dla zrozumienia jest przeznaczony do pracy dobrze przy przestrzeganiu prawidłowego podpisu. Dzięki sztuczce z projekcjami Either (która ma również swoje zalety), otrzymujemy ten problem.

+1

Ach, to zaczyna mieć sens. Masz kilka literówek, które początkowo mnie potknęły (brakujące liczby pierwsze, delikatne błędy itp.), Które wkrótce sprzątnę. – srparish

+0

Interesującym wynikiem powyższego jest to, że jeśli mam stałe monadyczne typy, wszystko będzie dobrze. Więc jeśli zależy mi tylko na właściwych projekcjach, mogę stworzyć niejawne, takie jak poniższe, które naprawia problem: implicit def RightProjection [A, B] (v: Albo [A, B]): Either.RightProjection [A, B] = v.right – srparish

+1

Jeszcze raz wielkie dzięki za dokładny zapis! Przeczytałem odnośnik i wciąż nie wiedziałem, co się dzieje. Oczywiście w tym prostym przykładzie, a * b może być przeniesiony na wydajność, ale jeśli to jest zastąpione złożonym obliczeniem, które musi zostać przekazane do funkcji, która zwraca inną (albo powtórzyć to kilka razy), będąc w stanie użyć pośredniego przypisanie wartości może być znacznie czystsze, a następnie mieć wiele poziomów łańcuchów dla instrukcji/yield. – srparish

Powiązane problemy