2011-10-07 8 views
6

Trong ví dụ mã sau, tôi không hiểu tại sao hàm fun có thể được chuyển thành đối số cho phương thức addAction. Phương thức fun thuộc loại Unit, trong khi phương thức addAction mong đợi một chức năng thuộc loại () => Unit.Phương thức và phương thức Scala dưới dạng tham số

Nếu fun là loại () => Unit, thì tại sao không trình biên dịch phàn nàn rằng fun là loại Unit, khi tôi cố gắng thêm fun đến những hành động danh sách: actions = fun :: actions?

package myscala 

object MyScala { 

    def fun() { println("fun1 executed.") } 

    def addAction(a:() => Unit) { 
    actions = a :: actions 
    } 

    var actions: List[() => Unit] = List() 

    def main(args: Array[String]) { 
    // the following line would produce a compiler error (found: Unit, required:() => Unit), it's OK 
    // actions = fun :: actions 
    actions = (() => fun) :: actions // OK 
    // I would expect the same compiler error here (found: Unit, required:() => Unit), but it's OK why? 
    addAction(fun) 
    actions.foreach(_()) // prints twice "fun1 executed" 
    } 
} 

Trả lời

8

Cầm lấy cái này làm ví dụ giới thiệu:

def fun() { println("fun1 executed.") } 

val a1 = fun 
val a2:() => Unit = fun 

Cả hai dòng biên dịch và (nhờ gõ suy luận) họ nhìn tương đương. Tuy nhiên a1 là loại Unit trong khi a2 thuộc loại () => Unit ... Làm thế nào điều này có thể?

Vì bạn không cung cấp một cách rõ ràng loại a1, trình biên dịch diễn giải fun như một phương pháp fun tiếng gọi của loại Unit, vì thế mà loại a1 cũng giống như loại fun. Nó cũng có nghĩa là dòng này sẽ in fun1 được thi hành.

Tuy nhiên, a2 đã khai báo rõ ràng loại () => Unit. Trình biên dịch giúp bạn ở đây và nó hiểu rằng vì ngữ cảnh yêu cầu một hàm kiểu () => Unit và bạn đã cung cấp một phương thức khớp với kiểu này, nó không nên gọi phương thức đó, nhưng coi nó là hàm lớp đầu tiên!

Bạn không phải cam chịu chỉ định loại a1 một cách rõ ràng. Nói:

val a1 = fun _ 

Bây giờ bạn có hiểu vấn đề của mình ở đâu không?

+0

Có, tôi có. Điều này bây giờ có vẻ hiển nhiên đối với tôi (nó không hề có lúc đó). Khi trình biên dịch có thể suy ra rằng một kiểu hàm được mong đợi, tôi có thể chỉ cần viết 'fun', nếu không tôi phải làm cho nó rõ ràng rằng tôi đang truyền một hàm. Cảm ơn các câu trả lời rõ ràng cho tất cả các bạn! – Manu

+0

@Manu: Xem xét chấp nhận câu trả lời mà bạn cho là câu trả lời hay nhất (không nhất thiết là câu trả lời này) –

5

Bạn cần viết fun _ trong trường hợp đầu tiên để tránh gọi phương thức và thực hiện mở rộng eta thay thế.

này sẽ làm việc:

actions = (fun _) :: actions 

Nếu bạn không làm điều này, sau đó fun được đánh giá.

Để biết thêm chi tiết, xem Phần 6.7 (Giá trị phương pháp) của số Scala Language Reference.

Vì lý do tại sao fun không được đánh giá trong trường hợp thứ hai, đó là vì suy luận kiểu có thể kết luận rõ ràng rằng addAction mong đợi một hàm. Nhân tiện, loại fun về mặt kỹ thuật là ()Unit, không phải là Unit, nghĩa là loại phương thức chứ không phải loại giá trị. Xem Phần 3.3.1 trong mục reference để biết thêm.

+3

Tôi muốn đề nghị một đọc nhẹ: [Lập trình trong Scala Chương 9] (http://www.artima.com/pins1ed/control-abstraction.html) – Jamil

3

Có sự khác biệt giữa các phương pháp và chức năng. Trong trường hợp của bạn actions là danh sách các chức năng.Khi trình biên dịch biết rằng một hàm được yêu cầu (như trong trường hợp của addAction) nó có thể tự động chuyển đổi một phương thức fun thành một hàm. Bây giờ :: cũng là một phương pháp, do đó trình biên dịch cũng biết rằng nó có chức năng như các tham số. Nhưng vấn đề là đường cú pháp của toán tử liên kết bên phải ::. Nếu bạn gọi nó như một phương thức: actions.::(fun) nó sẽ biên dịch (mặc dù tôi không thể kiểm tra nó vào lúc này). Khi viết fun :: actions trình biên dịch nghĩ rằng fun là một biểu thức và do đó đánh giá nó và vì nó "trả về" một Unit bạn nhận được lỗi trình biên dịch của bạn.

EDIT

Vì tôi bây giờ có khả năng để kiểm tra giả thuyết của tôi (đó là sai) đây là lựa chọn của bạn:

// Usual syntax 
actions.::[() => Unit](fun) 
actions.::(fun:() => Unit) 
actions.::(fun _) 
// Operator syntax 
(fun:() => Unit) :: actions 
(fun _) :: actions 
+0

'actions.: :(fun)' đánh giá thành 'List [Any] = List (())'. Nó vẫn còn đánh giá các chức năng mặc dù. Tự hỏi tại sao Danh sách [() => Đơn vị] được chuyển đổi thành Danh sách [Bất kỳ] nào? co-variance? – Jamil

+2

Vâng, 'List [Any]' là kiểu chính xác nhất cho một danh sách có thể chứa 'Unit's và'() => Unit's. 'Unit', được xem là'() 'là những gì đánh giá' fun' trả về. – Philippe

+0

Tôi đã chỉnh sửa câu trả lời của mình. – agilesteel