2010-04-21 14 views
8

Ten prosty test, oczywiście, działa zgodnie z oczekiwaniami:Scala stawia pierwszeństwo niejawnej konwersji na "naturalne" operacje ... Dlaczego? Czy to błąd? Czy robię coś złego?

 
scala> var b = 2 
b: Int = 2 

scala> b += 1 

scala> b 
res3: Int = 3 

Teraz przynieść to w zakresie:

 
class A(var x: Int) { def +=(y:Int) { this.x += y } } 
implicit def int2A(i:Int) : A = new A(i)    

jestem definiowania nowej klasy i + = praca na nim, i wygodna niejawna konwersja dla tych czasów, kiedy chcę dodać wartość Int do A Int.

Nigdy się nie spodziewałem, że to wpłynie na sposób, w jaki zachowują się moje zwykłe operacje Int, kiedy klasa "A" wcale nie jest częścią wyrażenia.

Ale robi:

 
scala> var b:Int = 0 
b: Int = 0 

scala> b += 1 

scala> b 
res29: Int = 0 

scala> b += 2 

scala> b 
res31: Int = 0 

Co wydaje się być tutaj dzieje jest to, że b: int jest niejawnie konwertowane do klasy „A”, który nie jest związany z żadną zmienną, a następnie + = jest wywoływany na nim, odrzucając wyniki.

Scala wydaje się dawać wysoki priorytet niejawnej konwersji ponad naturalnym + = zachowaniem (magia kompilatora, a nie faktyczna metoda), która jest już zdefiniowana jako Ints. Zdrowy rozsądek jak również tło w języku C++ mówi mi, że implicite należy wywoływać tylko w ostateczności, gdy kompilacja w przeciwnym razie byłaby nieudana. To prowadzi do wielu pytań ...

  • Dlaczego? Czy to błąd? Czy to projekt?
  • Czy istnieje obejście (inne niż niewykorzystywanie "+ =" dla operacji "+ =" mojego DSL)?

Dzięki

+0

'scala -Xprint: typer' mówi mi, że robi dokładnie to, co powiedziałeś - używając niejawnej konwersji, aby mogła wywołać' + = '. –

Odpowiedz

1

Nawet pomimo wyjaśnienia Eastsun, wygląda na to, że jest to błąd i powinien wypróbować transformację b=b+1 przed próbą niejawnej konwersji dla +=.

Proszę zadać to pytanie na listę e-mailową użytkownika scala, wysyłając wiadomość e-mail na adres [email protected] lub odwiedzając stronę internetową n4.nabble.com/Scala-User-f1934582.html. Jeśli jest to błąd, to tam zostanie zauważony i naprawiony.

+0

Dzięki ... Błąd Scala # 3377 –

+0

https://github.com/scala/bug/issues/3377 –

3

Nie sądzę, że to błąd. W rzeczywistości Int ma tylko metodę "+", ale nie ma metody "+ =". b + = 1 przekształciłby się na b = b + 1 w czasie kompilacji, jeśli nie istnieje inna domena, która ma metodę "+ =".

+0

Myślę, że nawet niezgodnie z wytycznymi jest to prawdopodobnie błąd i powinien spróbować transformacji przed próbą niejawnej konwersji dla + = –

6

od programowania w Scali, rozdział 17:

Kiedy piszesz + = b, a nie nie obsługuje metodę o nazwie + =, Scala postara interpretując je jako a = a + b .

Klasa Int nie zawiera metody +=. Jednak klasa A zapewnia metodę +=. Może to być przyczyną niejawnej konwersji z Int na A.

+0

Dzięki. Zaczynam się bardzo rozczarowywać ilością "magii", która została wprowadzona w coś, co miało być "ortogonalnym, czystym językiem". Powinni po prostu zaimplementować + = w Int i pozbyć się magii kompilatora. Mam nadzieję, że pojawi się Scala 3.0 :) –

+3

@Alex Jak zaimplementować '+ =' na niezmiennej klasie? –

+1

@Ben: +1, dobry punkt. – missingfaktor

1

Scala wydaje się dawać większy priorytet ukrytą konwersji w naturalnym += już zdefiniowanego do Int s.

O której wersji Scali mówisz? Nie znam żadnej wersji, która ma metodę += na Int. Z pewnością żadna z nadal obsługiwanych wersji nie musi być starą wersją, która musi tam być.

A ponieważ nie ma += na Int, ale jesteś nazywając metodę += na Int, Scala stara się spełniać tego typu ograniczenie poprzez niejawna konwersja.

+0

Edytowałem moje pytanie, aby wyjaśnić Int. + = Jest magią kompilatora, a nie faktyczną metodą. –

13

Jak zauważyli inni, Int nie może mieć + = "metody", ponieważ Int jest niezmienne. Zamiast tego, x + = 1 jest traktowane jako krótka forma dla x = x + 1, ale tylko wtedy, gdy nie ma metody o nazwie + =, która jest zdefiniowana na typie. Dlatego pierwszeństwo ma metoda rozstrzygania.

Biorąc pod uwagę, że Scala pozwala zdefiniować metody + =, a także pozwala na + = na zmiennych, czy moglibyśmy zmienić priorytet dwóch? To znaczy. spróbuj rozwinąć + = pierwszy i tylko wtedy, gdy to nie powiedzie się dla metody o nazwie + =?

Teoretycznie tak, ale twierdzę, że byłby on gorszy niż obecny system. Praktycznie nie. W bibliotece kolekcji Scali znajduje się wiele typów, które definiują zarówno metodę + dla nieinwazyjnego dodawania, jak i metodę + dla destrukcyjnego dodawania. Gdybyśmy włączony priorytet wokół następnie wywołanie jak

 
    myHashTable += elem 

rozwinie się do

 
    myHashTable = myHashTable + elem 

Tak byłoby skonstruować nowy hashtable i przypisać to z powrotem do zmiennej, zamiast po prostu aktualizowania elementu. To nie jest mądra rzecz ...

+1

Ale czy nie moglibyśmy spróbować uruchomić '+ =' myHashTable, a widząc, że go nie ma, rozwijamy go do 'myHashTable = myHashTable + elem', a jeśli nie powrócimy do niejawnych konwersji? –

+1

To jeszcze bardziej komplikowałoby sprawę. I unieważniłoby klasę użytecznych refaktoryzacji, gdzie dziedziczenie zostało zastąpione widokami. –

+0

Zgadzam się, że próba znalezienia metody + = przed rozwinięciem do "... = ... + ..." ma sens. To, czego nie zgadzam się, to wymuszenie niejawnej konwersji, aby znaleźć a + = w klasie, która jest "niezwiązana" z danym wyrażeniem. Innym podejściem byłoby formalne zdefiniowanie + = w Int, tak aby zostało "znalezione" wcześniej (przed niejawną konwersją), ale należy użyć specjalnej składni, aby wskazać, że jest to "nie jest prawdziwa metoda" i jawnie powiedzieć kompilatorowi, aby cofnął się do magii ekspansji . BTW Jestem zaszczycony, aby uzyskać odpowiedź bezpośrednio z góry :) Dzięki –

Powiązane problemy