2013-07-19 26 views
25

Tại sao câu lệnh trả về rõ ràng (một từ sử dụng từ khóa return) trong hàm trả về ẩn danh từ hàm được đặt tên kèm theo chứ không chỉ từ chính hàm ẩn danh?Tuyên bố trả về Scala trong các hàm ẩn danh

Ví dụ: kết quả chương trình sau đây trong một lỗi type:

def foo: String = { 
    ((x: Integer) => return x) 
    "foo" 
} 

Tôi biết nó được đề nghị để tránh những từ khóa return, nhưng tôi quan tâm đến lý do tại sao các báo cáo lợi nhuận ngầm và rõ ràng có một ngữ nghĩa khác nhau trong các chức năng ẩn danh.

Trong ví dụ sau, câu lệnh trả về "tồn tại" sau m đã hoàn tất việc thực thi và chương trình dẫn đến ngoại lệ trong thời gian chạy. Nếu các hàm ẩn danh không trả về từ hàm kèm theo, nó sẽ không thể biên dịch mã đó.

def main(args: Array[String]) { 
    m(3) 
} 

def m: (Integer => Unit) = 
    (x: Integer) => return (y: Integer) => 2 

Trả lời

16

trở lại nói Chính thức được định nghĩa như mọi khi trở về từ kèm theo khu vực gần tên là phương pháp

A return expression return e must occur inside the body of some enclosing named method or function. The innermost enclosing named method or function in a source program, f , must have an explicitly declared result type, and the type of e must conform to it. The return expression evaluates the expression e and returns its value as the result of f . The evaluation of any statements or expressions following the return expression is omitted.

Vì vậy, nó không có ngữ nghĩa khác nhau trong một lambda. Các nếp nhăn là, không giống như một phương pháp bình thường, một đóng cửa được tạo ra từ một lambda có thể thoát khỏi một cuộc gọi đến phương pháp kèm theo và bạn có thể nhận được một ngoại lệ nếu có sự trở lại trong một đóng cửa như vậy.

If the return expression is itself part of an anonymous function, it is possible that the enclosing instance of f has already returned before the return expression is executed. In that case, the thrown scala.runtime.NonLocalReturnException will not be caught, and will propagate up the call stack.

Bây giờ, cũng như "lý do". Một lý do nhỏ hơn là thẩm mỹ: lambdas là các biểu thức và nó đẹp khi một biểu thức và tất cả các biểu thức con của nó có cùng ý nghĩa cho dù cấu trúc làm tổ là gì. Neal Gafter nói về điều đó tại http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

Lý do chính nó tồn tại, cho phép bạn dễ dàng mô phỏng các luồng kiểm soát thường được sử dụng trong lập trình bắt buộc nhưng vẫn cho phép bạn tóm tắt mọi thứ thành các hàm bậc cao hơn. Như một ví dụ đồ chơi, cấu trúc foreach của Java (for (x : xs) { yada; }) cho phép trả về bên trong vòng lặp. Scala không có trình độ ngoại ngữ. Thay vào đó, nó đặt foreach trong thư viện (không đếm "cho biểu hiện" mà không có năng suất vì họ chỉ desugar để foreach). Có một sự trở lại không phải là địa phương có nghĩa là bạn có thể lấy một Java foreach và dịch trực tiếp tới một Scala foreach.

BTW, Ruby, Smalltalk và Common Lisp (ngoài đỉnh đầu của tôi) cũng có các kết quả "không phải cục bộ" tương tự.

+0

Bạn có một số ví dụ nghiêm túc hơn về lý do tại sao sự khác biệt trong ngữ nghĩa là cần thiết? Bởi vì những gì bạn đã liệt kê có thể dễ dàng được mô phỏng với một 'foreach' đã sửa đổi có tham số biến vị ngữ. – corazza

3

Từ khóa return được dành riêng cho (lớp), không thể sử dụng trong chức năng. Bạn có thể dễ dàng kiểm tra rằng:

object Foo { 
    val bar = (i: Int) => return i + i 
} 

Điều này cho phép

<console>:42: error: return outside method definition 
     object Foo { val bar = (i: Int) => return i + i } 
             ^

Chủ yếu là bạn có thể đối xử với phương pháp và chức năng như nhau, vì phương pháp của hàm apply hành xử cú pháp như gọi một phương thức, và cái gọi là sự mở rộng eta cho phép một phương thức được truyền vào như một đối số hàm.

Trong trường hợp này, nó tạo sự khác biệt. Khi xác định như phương pháp, nó là hợp pháp:

object Foo { 
    def bar(i: Int): Int = return i + i 
} 

Nói tóm lại, bạn chỉ nên sử dụng return trong các phương pháp cho phép có điều kiện (sớm) trả về. Xem this post để thảo luận về các phương pháp so với hàm.

+0

Câu trả lời này không thực sự trả lời * tại sao * tức là lý do thiết kế ngôn ngữ đằng sau quyết định đó. – corazza

+0

@jcz điều này sẽ cực kỳ mờ và không trực quan. Hãy nghĩ về lambdas, ví dụ: 'xs.foreach {x => nếu (x == y) trả về x}' và tương tự. –

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