2013-02-10 26 views
20

Trong các ngôn ngữ như SML, Erlang và trong Buch của người khác chúng ta có thể xác định các chức năng như thế này:Có bất kỳ hạn chế cơ bản nào ngăn Scala triển khai thực hiện mẫu so khớp các hàm không?

fun reverse [] = [] 
| reverse x :: xs = reverse xs @ [x]; 

tôi biết chúng tôi có thể viết tương tự tại Scala như thế này (và tôi biết, có rất nhiều lỗ hổng trong các mã dưới đây):

def reverse[T](lst: List[T]): List[T] = lst match { 
    case Nil  => Nil 
    case x :: xs => reverse(xs) ++ List(x) 
} 

Nhưng tôi tự hỏi, nếu chúng ta có thể viết mã cũ trong Scala, có lẽ với desugaring sau này.

Có hạn chế cơ bản nào cho cú pháp được triển khai trong tương lai hay không.

UPD
Dưới đây là một đoạn trích về cách nó có thể trông giống như:

type T 
def reverse(Nil: List[T]) = Nil 
def reverse(x :: xs: List[T]): List[T] = reverse(xs) ++ List(x) 
+0

Bạn không muốn viết nó như thế này, vì đây là một 'đảo ngược' khá không hiệu quả. – Ingo

+2

@ Tức là nó chỉ là một ví dụ (và tôi đã thêm tuyên bố từ chối trách nhiệm về độ âm của mã), tôi có thể viết giai thừa như một ví dụ và nó sẽ khá hiệu quả. Hay bạn ám chỉ điều gì khác? –

+0

Đối với tôi, hai cái nhìn khá giống nhau ngoại trừ Scala đòi hỏi các đối số phải được chú thích kiểu, là kết quả của giống lai OOP/Functional (so với Hindley-Milner trong SML). Vì vậy, tôi không chính xác những gì bạn muốn xem (ngoài việc thay đổi cú pháp của Scala, như 'def' ->' fun', 'Nil' ->' [] ') –

Trả lời

14

Nó thực sự phụ thuộc vào những gì bạn có nghĩa là cơ bản.

Nếu bạn đang thực sự hỏi "nếu có một showstopper kỹ thuật mà sẽ ngăn chặn để thực hiện tính năng này", sau đó tôi sẽ nói câu trả lời là không . Bạn đang nói về desugaring, và bạn đang đi đúng hướng ở đây. Tất cả những gì cần làm là cơ bản khâu vài trường hợp phân tách thành một hàm duy nhất, và điều này có thể được thực hiện như là một bước tiền xử lý (điều này chỉ đòi hỏi kiến ​​thức cú pháp, không cần kiến ​​thức ngữ nghĩa).Nhưng đối với điều này thậm chí có ý nghĩa, tôi sẽ xác định một vài quy tắc:

  • Chữ ký hàm là bắt buộc (trong Haskell, ví dụ, tùy chọn này sẽ là tùy chọn cho dù bạn đang xác định hàm cùng một lúc hoặc trong một số phần). Chúng tôi có thể cố gắng sắp xếp để sống mà không có chữ ký và cố gắng để trích xuất nó từ các phần khác nhau, nhưng thiếu thông tin loại sẽ nhanh chóng đến để byte chúng tôi. Một đối số đơn giản hơn là nếu chúng ta cố gắng suy ra một chữ ký ngầm, chúng ta cũng có thể làm điều đó cho tất cả các phương thức. Nhưng sự thật là có những lý do rất tốt để có chữ ký rõ ràng trong scala và tôi không thể tưởng tượng để thay đổi điều đó.
  • Tất cả các phần phải được xác định trong cùng một phạm vi. Để bắt đầu, chúng phải được khai báo trong cùng một tệp vì mỗi tệp nguồn được biên dịch riêng biệt và do đó một bộ tiền xử lý đơn giản sẽ không đủ để thực hiện tính năng này. Thứ hai, chúng tôi vẫn kết thúc với một phương pháp duy nhất cuối cùng, vì vậy nó chỉ tự nhiên để có tất cả các phần trong cùng một phạm vi.
  • quá tải là không thể cho các phương pháp như vậy (nếu không chúng tôi sẽ cần phải lặp lại chữ ký cho mỗi phần chỉ để Preprocessor biết đó là một phần thuộc về mà quá tải)
  • Linh kiện được thêm vào (khâu) đến tạo match trong thứ tự mà chúng được khai báo

Vì vậy, đây là cách nó có thể trông giống như:

def reverse[T](lst: List[T]): List[T] // Exactly like an abstract def (provides the signature) 
// .... some unrelated code here... 
def reverse(Nil) = Nil 
// .... another bit of unrelated code here... 
def reverse(x :: xs) = reverse(xs) ++ List(x) 

Mà có thể được trivially chuyển thành:

def reverse[T](list: List[T]): List[T] = lst match { 
    case Nil  => Nil 
    case x :: xs => reverse(xs) ++ List(x) 
} 
// .... some unrelated code here... 
// .... another bit of unrelated code here... 

Dễ dàng thấy rằng phép biến đổi trên rất cơ học và có thể thực hiện bằng cách thao tác nguồn AST (AST được tạo ra bởi ngữ pháp được sửa đổi một chút chấp nhận cấu trúc mới này) và biến nó thành AST mục tiêu (AST được tạo ra bởi ngữ pháp scala chuẩn). Sau đó, chúng tôi có thể biên dịch kết quả như bình thường.

Vì vậy, có bạn đi, với một vài quy tắc đơn giản, chúng tôi có thể triển khai bộ tiền xử lý thực hiện tất cả công việc để triển khai tính năng mới này.


Nếu bởi cơ bản bạn đang yêu cầu "là có bất cứ điều gì mà sẽ làm cho tính năng này ra khỏi nơi", sau đó nó có thể lập luận rằng điều này không cảm thấy rất scala. Nhưng nhiều hơn đến điểm, nó không mang lại nhiều đến bàn. Tác giả Scala thực sự có xu hướng hướng ngôn ngữ đơn giản hơn (như các tính năng ít tích hợp hơn, cố gắng di chuyển một số tính năng tích hợp vào thư viện) và thêm cú pháp mới không thực sự dễ đọc hơn so với mục tiêu đơn giản hóa.

3

Tôi không biết SML hoặc Erlang, nhưng tôi biết Haskell. Nó là một ngôn ngữ không có quá tải phương thức. Phương pháp quá tải kết hợp với mô hình phù hợp như vậy có thể dẫn đến sự mơ hồ. Hãy tưởng tượng mã sau:

def f(x: String) = "String "+x 
def f(x: List[_]) = "List "+x 

Điều đó có nghĩa là gì? Nó có thể có nghĩa là quá tải phương thức, tức là phương pháp được xác định trong thời gian biên dịch. Nó cũng có thể có nghĩa là khớp mẫu. Sẽ chỉ có một phương thức f (x: AnyRef) sẽ thực hiện việc so khớp.

Scala cũng đã đặt tên tham số, có thể cũng sẽ bị hỏng.

Tôi không nghĩ rằng Scala có thể cung cấp cú pháp đơn giản hơn bạn đã thể hiện nói chung. Cú pháp đơn giản hơn có thể IMHO chỉ hoạt động trong một số trường hợp đặc biệt.

3

Có ít nhất hai vấn đề:

  1. [] được dành riêng nhân vật bởi vì chúng được sử dụng cho các đối số kiểu. Trình biên dịch cho phép các khoảng trống xung quanh chúng, do đó sẽ không phải là một lựa chọn.
  2. Vấn đề khác là = trả về Unit. Vì vậy, các biểu hiện sau khi | sẽ không quay trở lại bất kỳ kết quả

Gần nhất tôi có thể đưa ra là thế này (lưu ý rằng rất chuyên đối với ví dụ của bạn):

// Define a class to hold the values left and right of the | sign 
class |[T, S](val left: T, val right: PartialFunction[T, T]) 

// Create a class that contains the | operator 
class OrAssoc[T](left: T) { 
    def |(right: PartialFunction[T, T]): T | T = new |(left, right) 
} 

// Add the | to any potential target 
implicit def anyToOrAssoc[S](left: S): OrAssoc[S] = new OrAssoc(left) 

object fun { 

    // Use the magic of the update method 
    def update[T, S](choice: T | S): T => T = { arg => 
    if (choice.right.isDefinedAt(arg)) choice.right(arg) 
    else choice.left 
    } 
} 

// Use the above construction to define a new method 
val reverse: List[Int] => List[Int] = 
    fun() = List.empty[Int] | { 
    case x :: xs => reverse(xs) ++ List(x) 
    } 

// Call the method 
reverse(List(3, 2, 1)) 
5

Trong SML, đoạn mã của bạn là theo nghĩa đen đường chỉ là cú pháp (gọi là "hình thức có nguồn gốc" trong thuật ngữ của spec ngôn ngữ) cho

val rec reverse = fn x => 
    case x of [] => [] 
      | x::xs = reverse xs @ [x] 

mà rất gần với mã Scala bạn hiển thị. Vì vậy, không có lý do "cơ bản" mà Scala không thể cung cấp cùng một loại cú pháp. Vấn đề chính là nhu cầu của Scala đối với nhiều chú thích kiểu, điều này làm cho cú pháp viết tắt này kém hấp dẫn hơn nói chung, và có lẽ không đáng giá trong thời gian này.

Cũng lưu ý rằng cú pháp cụ thể mà bạn đề xuất sẽ không bay tốt, vì không có cách nào để phân biệt một định nghĩa hàm theo từng trường hợp với hai hàm bị quá tải theo cú pháp. Bạn có thể cần một số cú pháp thay thế, tương tự như SML sử dụng "|".

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