2012-01-14 32 views
9

Tôi nghĩ sẽ dễ dàng hơn khi giải thích nó bằng một ví dụ đơn giản. (giúp rephrasing tiêu đề được chào đón ;-)Scala: Có thể chỉ ra một lớp chung mà thực hiện một phương thức nhất định

Tôi muốn thực hiện phương thức squared và sử dụng implicit def, tự động thêm nó vào bất kỳ lớp nào hỗ trợ * -operator.

Với một Int nó rất dễ dàng:

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

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

Nhưng với Bất kỳ hoặc AnyVal tôi nhận được lỗi sau:

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 } 

Tôi muốn biết làm thế nào tôi có thể áp dụng nó cho bất kỳ số hoặc thậm chí tốt hơn cho bất kỳ lớp học nào hỗ trợ * -operator.

+0

+1 câu hỏi hay. Đây là loại thứ Scala mà tôi gặp rắc rối. Trong Haskell, tôi chỉ sử dụng ': t \ x -> x * x' trong ghci (và nó cho tôi biết' Num a => a -> a'), nhưng trong Scala, khi tôi nhập 'x => x * x' trong REPL, nó phàn nàn rằng tôi đã không cho 'x' một kiểu. Và rồi tôi vẫy tay xung quanh và nhắc nhở bản thân rằng hệ thống kiểu của Scala mạnh hơn Haskell. –

+0

Nó có thể hoạt động với Numeric thay vì AnyVal. Bạn nên xem cách Seq.product() quản lý để có được quyền truy cập vào phép nhân. – scand1sk

+0

@DanBurton: Có lẽ bạn đã không giải thích câu hỏi một cách chính xác. Xem câu trả lời của Dan Simon dưới đây. – missingfaktor

Trả lời

8

Nó không thể có giải pháp hoạt động trên bất kỳ loại với một phương pháp * mà không cần viết một chuyển đổi soạn sẵn cho từng loại bạn muốn để đối phó với. Về cơ bản để làm điều đó bạn sẽ cần một kiểu cấu trúc đệ quy, và Scala không hỗ trợ những loại đó vì việc loại bỏ kiểu JVM. Xem phần này post để biết thêm chi tiết.

Bạn có thể nhận được khá gần với những gì bạn muốn bằng cách sử dụng loại lớp cùng với loại lớp học Numeric, (lấy cảm hứng từ câu trả lời cho câu hỏi thisthis). Điều này sẽ làm việc với hầu hết các nguyên thủy:

//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

Grate, tôi đã thử nó và nó hoạt động, đó là lý do tại sao tôi đánh dấu nó là câu trả lời đúng ... nhưng có một vài điều tôi đang thiếu ... Bạn có ghi đè toán tử "*" cho mọi lớp Numeric không ?? ? – opensas

+1

Sắp xếp, nó chỉ cung cấp triển khai cho một giao diện hơn là ghi đè. Đặc điểm 'Multipliable' được cho là đại diện cho bất kỳ kiểu nào có một số khái niệm về phép nhân. Đối với các kiểu số, chúng ta về cơ bản nói rằng phép nhân này nên là phép nhân "thường xuyên" thực tế, nhưng chúng ta có thể đã định nghĩa nó một cách khác nhau. '3 * 3' vẫn sử dụng' * 'được định nghĩa trong' Int' –

0

Hãy nghĩ về điều đó. Phép nhân có các thuộc tính khác nhau trên các đối tượng khác nhau. Phép nhân số nguyên, ví dụ, là kết hợp và giao hoán, nhân vô hướng không phải là giao hoán. Và nhân trên số dấu chấm động ... Nhưng, tất nhiên, bạn có thể có những gì bạn muốn với đặc điểm hoặc với "đặc điểm và ngầm" xây dựng tương tự như một typeclass.

Các vấn đề liên quan