2009-08-27 9 views
15

Próbuję utworzyć niejawna konwersja z dowolnego typu (powiedzmy, int) na ciąg ...Unikanie niejawny def niejasności w Scala

niejawna konwersja do String oznacza RichString metody (takie jak odwrocie) nie są dostępne .

implicit def intToString(i: Int) = String.valueOf(i) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => error: value reverse is not a member of Int 
100.length // => 3 

Pośrednia konwersja do RichString oznacza metody łańcuchowe (np toCharArray) nie są dostępne

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.reverse // => "001" 
100.toCharArray // => error: value toCharArray is not a member of Int 
100.length // => 3 

obiema ukryte konwersji oznacza metodę podwajania (np długości) są niejednoznaczne.

implicit def intToString(i: Int) = String.valueOf(i) 
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => "001" 
100.length // => both method intToString in object $iw of type 
    // (Int)java.lang.String and method intToRichString in object 
    // $iw of type (Int)scala.runtime.RichString are possible 
    // conversion functions from Int to ?{val length: ?} 

Czy możliwe jest niejawne przekonwertowanie na ciąg i nadal obsługuje wszystkie metody String i RichString?

Odpowiedz

2

Albo stanowić ogromną klasę proxy lub ssać go i wymaga klienta, aby go disambiguate:

100.asInstanceOf [ciąg] .length

+0

Wygląda na to, że "ssać to" jest najlepszą opcją. Projektowanie interfejsu API w Scali jest znacznie trudniejsze niż bycie konsumentem API. Chciałem stworzyć typ danych, który zawija wartość i pozwala użytkownikowi API traktować ten typ danych tak, jakby był typem internowanej wartości. Na przykład. jeśli opakuje Int, traktuj obiekt jak int. Okazuje się, że to zbyt trudne. Zastanawiam dając moje dataType metody pomocnika: typ T val internedValue: T def s = internedValue.asInstanceOf [String] def i = internedValue.asInstanceOf [Int] ... itd – Synesso

+20

Wolę (' 100: String) .length', ponieważ jest to bezpieczne, podczas gdy 'asInstanceOf' nie jest. Poza tym jest ładniejsza i krótsza. –

+2

Nie tylko to, ale asInstanceOf nigdy tu nie będzie działało, ponieważ zdarza się tylko downcasting (co tutaj kończy się niepowodzeniem) i nie rozpocznie żadnej niejawnej konwersji. Innymi słowy, '100.asInstanceOf [String] .length' zawsze zawiedzie z' ClassCastException' –

2

Jedyną dostępną opcją jest utworzenie nowej klasy MyString klasy String Wrapper i niech to wywoła jakakolwiek metoda, którą chcesz wywołać w niejednoznacznym przypadku. Następnie możesz zdefiniować niejawne konwersje do MyString i dwie niejawne konwersje z MyString do String i RichString, na wypadek gdybyś musiał przekazać je do funkcji bibliotecznej.

+0

To brzmi jak uczciwe podejście, ale dużo pracy. Ponadto podejrzewam, że nie jest to dowód na przyszłość, ponieważ wprowadzono inne niejawne konwersje. – Synesso

+0

Pewnie jest dużo pracy, ale nie wiele myślenia, po prostu pisania. ;) A dlaczego nie miałoby to być przyszłym dowodem? Nikt nie zmusza Cię do korzystania z żadnych niejawnych konwersji, które mogą zostać wprowadzone w przyszłości, nawet tych z obciążenia wstępnego. –

+0

@Kim Stebel: 'Oczywiście, że jest dużo pracy, ale nie wiele myślenia, po prostu wpisując' programistów są leniwi myśliciele, a nie pracowici maszynistki. –

1

Jestem zdezorientowany: nie możesz użyć .toString na każdym typie mimo to unikając potrzeby niejawnych konwersji?

+0

Nie sądzę, że to by zmieniło sprawy, z wyjątkiem być może wprowadzenia wyjątków wskaźnika pustego. Aby było jasne, proponujesz zmianę String.valueOf (i) na i.toString? – Synesso

5

nie mam rozwiązanie, ale będzie komentować że metody RichString powodują, że Scala nie łączy niejawnych wywołań (zobacz 21.2 "Reguły dla implicitów" w Programowanie w Scala).

Jeśli wprowadzisz półprodukt String, Scala wykona konwersję implikowaną do RichString (która jest zdefiniowana w Predef.scala).

przykład

$ scala 
Welcome to Scala version 2.7.5.final [...]. 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> implicit def intToString(i: Int) = String.valueOf(i) 
intToString: (Int)java.lang.String 

scala> val i = 100 
i: Int = 100 

scala> val s: String = i 
s: String = 100 

scala> s.reverse 
res1: scala.runtime.RichString = 001 
+0

Dobrze wiedzieć! Dziękuję Ci. – Synesso

+0

Czy jest to "tylko" wada dopuszczenia kołowych niejawnych relacji wymienialnych, czy istnieją inne powody? Ponieważ nawet jeśli masz okrąg, powinieneś statycznie znaleźć "najbliższą" implementację. Eksplorowanie wykresu bez dwukrotnego odwiedzania wykresów nie jest trudne, chociaż może trochę potrwać. – Raphael

5

jako Scala 2.8, zostało ulepszone. Zgodnie this paperUnikanie Ambiguities)

wcześniej, najbardziej, określonym metodą C przeciążony lub utajonego konwersji będą wybrane w oparciu wyłącznie o typy argumentów w metody. Dodano dodatkową klauzulę, która mówi, że najbardziej konkretna metoda nie może być zdefiniowana w odpowiedniej nadklasie żadnej z innych alternatyw. Ten schemat został zastąpiony w Scali 2.8 następującym, bardziej liberalnym: Podczas porównywania dwóch różnych stosowanych alternatyw dla przeciążonej metody lub niejawnej, każda metoda otrzymuje jeden punkt za więcej specyficznych argumentów , a inny punkt za jest określony w odpowiedniej podklasie. Alternatywny "wygrywa" nad drugim, jeśli uzyska większą liczbę punktów w tych dwóch porównaniach.Oznacza to w szczególności, że jeśli alternatywy mają identyczne typy argumentów, wygrywa ten, który jest zdefiniowany w podklasie .

Patrz przykład that other paper (§6.5).

+0

Twój link do pierwszego papieru jest uszkodzony. –

2

Przyjęte rozwiązanie (wysłane przez Mitcha Blevinsa) nigdy nie zadziała: downcasting Int na String przy użyciu asInstanceOf zawsze zawiedzie.

Jednym z rozwiązań tego problemu jest dodanie konwersja z dowolnego typu String-zamienny do RichString (czy raczej do StringOps jak jest teraz nazywa):

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x)) 

Następnie zdefiniować konwersję (ów) do ciąg jak poprzednio:

scala> implicit def intToString(i: Int) = String.valueOf(i) 
warning: there was one feature warning; re-run with -feature for details 
intToString: (i: Int)String 

scala> 100.toCharArray 
res0: Array[Char] = Array(1, 0, 0) 

scala> 100.reverse 
res1: String = 001 

scala> 100.length 
res2: Int = 3