2012-01-14 15 views
9

Myślę, że łatwiej jest wyjaśnić to na prostym przykładzie. (Pomoc przeformułowanie tytuł jest mile widziane ;-)Scala: Czy można wskazać klasę ogólną, która implementuje określoną metodę?

Chciałbym zaimplementować metodę squared i korzystając implicit def automatycznie dodać go do dowolnej klasy, który obsługuje * -operator.

Z Int to bardzo proste:

class EnhancedInt(x: Int) { def squared = x * x } 

implicit def IntToEnchancedInt(x: Int) = new EnhancedInt(x) 

Ale z żadną lub AnyVal pojawia się następujący błąd:

scala> class EnhanceAny(x: AnyVal) { def squared = x * x } 
<console>:7: error: value * is not a member of AnyVal 
     class EnhanceAny(x: AnyVal) { def squared = x * x } 

Chciałbym wiedzieć, w jaki sposób mogę zastosować go do dowolnego numeryczny klasa, a nawet lepiej, do każdej klasy obsługującej operatora-*.

+0

+1 dobre pytanie. Jest to rodzaj rzeczy z Scali, z którymi mam problem. W Haskell, po prostu użyłbym ': t \ x -> x * x' w ghci (i mówi mi" Num a => a -> a "), ale w Scali, kiedy wpisuję' x => x * x' w REPL, narzeka, że ​​nie podałem 'x' typu. A potem macham rękami i przypominam sobie, że system typu Scala jest bardziej "potężny" niż Haskella. –

+0

Może współpracować z Numeric zamiast AnyVal. Powinieneś zobaczyć, jak Seq.product() zarządza, aby uzyskać dostęp do mnożenia. – scand1sk

+0

@DanBurton: Być może nie poprawnie odczytałeś pytania. Zobacz odpowiedź Dana Simona poniżej. – missingfaktor

Odpowiedz

8

To nie jest możliwe, aby rozwiązanie, które działa na wszelkiego rodzaju z metodą * bez pisania konwersję szablonowe dla każdego rodzaju chcesz się zajmować. Zasadniczo do tego potrzebowałbyś strukturalnego typu rekursywnego, a Scala nie obsługuje tych z powodu usuwania typu JVM. Więcej informacji można znaleźć pod numerem post.

można dostać dość blisko tego, co chcesz za pomocą klasy typu wraz z klasą Numeric typu (zainspirowany odpowiedzi na pytanie this i this). Będzie działać z większością prymitywów:

//define the type class 
trait Multipliable[X] { def *(x: X): X} 

//define an implicit from A <% Numeric[A] -> Multipliable[Numeric[A]] 
implicit def Numeric2Mult[A](a: A)(implicit num: Numeric[A]): Multipliable[A] = new Multipliable[A]{def *(b: A) = num.times(a, b)} 

//now define your Enhanced class using the type class 
class EnhancedMultipliable[T <% Multipliable[T]](x: T){ def squared = x * x} 

//lastly define the conversion to the enhanced class 
implicit def Mult2EnhancedMult[T <% Multipliable[T]](x: T) = new EnhancedMultipliable[T](x) 

3.squared 
//Int = 9 

3.1415F.squared 
//Float = 9.869022 

123456789L.squared 
//Long = 15241578750190521 
+0

Kratka, spróbowałem i działa, dlatego oznaczyłem ją jako poprawną odpowiedź ... ale jest kilka rzeczy, których mi brakuje ... Czy zastępujesz operatora "*" dla każdej klasy liczbowej? ? – opensas

+1

W pewnym sensie chodzi raczej o zapewnienie implementacji interfejsu zamiast przesłonięcia. Cecha 'Multipliable' ma reprezentować dowolny typ, który ma pewne pojęcie mnożenia. W przypadku typów liczbowych mówimy zasadniczo, że to mnożenie powinno być rzeczywistym "regularnym" mnożeniem, ale moglibyśmy je zdefiniować inaczej. '3 * 3' nadal używa' * 'zdefiniowanego w' Int' –

0

Pomyśl o tym. Mnożenie ma różne właściwości na różnych obiektach. Na przykład mnożenie całkowite jest asocjacyjne i przemienne, mnożenie skalarne nie jest przemienne. I mnożenie liczb zmiennoprzecinkowych ... Ale oczywiście możesz mieć to, co chcesz, z cechą lub z "cechą i niejawną" konstrukcją, która przypomina typografię.

Powiązane problemy