2010-08-03 36 views
14

Trong Erlang, bạn được khuyến khích không khớp các mẫu mà bạn không thực sự xử lý. Ví dụ:Đối sánh mẫu, F # so với Erlang

case (anint rem 10) of 
    1 -> {ok, 10} 
    9 -> {ok, 25} 
end; 

là kiểu được khuyến khích, với kết quả có thể khác dẫn đến kết quả là badmatch. Điều này là nhất quán với triết lý "để nó sụp đổ" trong Erlang.

Mặt khác, F # sẽ phát hành "mẫu phù hợp không hoàn chỉnh" trong mã F # tương đương, như here.

Câu hỏi đặt ra: tại sao không F # loại bỏ các cảnh báo, một cách hiệu quả bằng cách làm tăng mỗi mô hình kết hợp với một tuyên bố tương đương với

|_ -> failwith "badmatch" 

và sử dụng "để cho nó sụp đổ" triết lý?

Chỉnh sửa: Hai câu trả lời thú vị cho đến nay: hoặc để tránh các lỗi có khả năng khi không xử lý tất cả các trường hợp của một kiểu dữ liệu đại số; hoặc vì nền tảng .Net. Một cách để tìm ra đó là kiểm tra OCaml. Vậy, hành vi mặc định trong OCaml là gì?

Chỉnh sửa: Để xóa sự hiểu lầm của .Net những người không có nền tảng trong Erlang. Quan điểm của triết lý Erlang là không tạo ra mã xấu luôn luôn bị treo. Hãy để nó sụp đổ có nghĩa là let some other process fix the error. Thay vì viết hàm để nó có thể xử lý tất cả các trường hợp có thể, hãy để người gọi (ví dụ) xử lý các trường hợp xấu được tự động ném. Đối với những người có nền Java, nó giống như sự khác biệt giữa việc có một ngôn ngữ với các ngoại lệ được kiểm tra, nó phải khai báo mọi thứ nó có thể trả về với mọi ngoại lệ có thể, và có một ngôn ngữ trong đó các hàm có thể tăng các ngoại lệ không được khai báo một cách rõ ràng.

+0

Tôi nghĩ triết lý là chức năng của bạn nên xử lý toàn bộ miền được mong đợi của nó, và trình biên dịch nhắc bạn về điều này. Nếu hàm không xử lý bất kỳ trường hợp nào nhiều hơn hai thì bạn nên hạn chế các đối số theo loại, nếu không thì hãy tính toán tất cả các đối số. Đó là sự hiểu biết của tôi; Tôi tự học về triết lý này. – grettke

+0

@grettke: bạn đã đúng. Triết lý này hoàn toàn trái ngược với triết lý Erlang. Và câu hỏi của tôi là tại sao triết lý này lại ưa thích (hoặc khi nào). Các câu trả lời dưới đây khá sáng. –

Trả lời

23

F # (và các ngôn ngữ khác có khớp mẫu, như Haskell và O'Caml) hoàn toàn thêm trường hợp ném ngoại lệ. Theo tôi, lý do có giá trị nhất để có các mẫu khớp hoàn chỉnh và chú ý đến cảnh báo, là nó giúp dễ dàng tái cấu trúc bằng cách mở rộng kiểu dữ liệu của bạn, vì trình biên dịch sẽ cảnh báo bạn về mã mà bạn chưa được cập nhật với trường hợp mới.

Mặt khác, đôi khi thực sự là những trường hợp cần được bỏ qua, và sau đó nó gây phiền nhiễu khi phải đặt trong một trường hợp bắt tất cả với những gì thường là một thông báo lỗi kém. Vì vậy, đó là một sự cân bằng.

Trong câu trả lời cho chỉnh sửa của bạn, đây cũng là cảnh báo theo mặc định trong O'Caml (và trong Haskell có -Wall).

+0

Cảm ơn. Tôi có thể thấy tái cấu trúc là một lý do chính đáng. Và cảm ơn thông tin OCaml/Haskell. –

13

Trong hầu hết các trường hợp, đặc biệt là với kiểu dữ liệu đại số, quên trường hợp có thể là một tai nạn và không phải là một quyết định có chủ ý bỏ qua một trường hợp. Trong các ngôn ngữ chức năng được đánh máy mạnh mẽ, tôi nghĩ rằng hầu hết các chức năng sẽ là tổng số, và do đó sẽ xử lý mọi trường hợp. Ngay cả đối với các chức năng một phần, thường lý tưởng để ném ngoại lệ cụ thể thay vì sử dụng lỗi khớp mẫu chung (ví dụ: List.head ném một số ArgumentException khi được cung cấp danh sách trống).

Vì vậy, tôi nghĩ rằng nó thường có ý nghĩa đối với trình biên dịch để cảnh báo cho nhà phát triển. Nếu bạn không thích hành vi này, bạn có thể thêm mẫu bắt tất cả tự ném ngoại lệ hoặc tắt hoặc bỏ qua cảnh báo.

11

tại sao không F # loại bỏ các cảnh báo

Thú vị mà bạn sẽ hỏi này. Nguồn tiêm thời gian chạy sai là hoàn toàn chống lại triết lý đằng sau F # và người thân của nó. Nó được coi là một sự ghê tởm kỳ cục. Họ ngôn ngữ này là tất cả về kiểm tra tĩnh, trong phạm vi hệ thống kiểu được thiết kế cơ bản để tạo điều kiện chính xác cho các loại kiểm tra tĩnh này.

Sự khác biệt rõ rệt về triết học này là lý do tại sao F # và Python hiếm khi được so sánh và tương phản. "Không bao giờ hai người sẽ gặp nhau" như họ nói.

Vì vậy, hành vi mặc định trong OCaml là gì?

Tương tự như F #: tính đầy đủ và dự phòng khớp mẫu được kiểm tra lúc biên dịch và cảnh báo được phát hành nếu kết quả trùng khớp. Phong cách thành ngữ cũng giống nhau: bạn được yêu cầu viết mã của bạn sao cho những cảnh báo này không xuất hiện.

Hành vi này không liên quan gì đến .NET và, trên thực tế, chức năng này (từ OCaml) chỉ được triển khai đúng trong F # khá gần đây.

Ví dụ, nếu bạn sử dụng một mô hình trong một let ràng buộc để trích xuất các phần tử đầu tiên của danh sách bởi vì bạn biết danh sách sẽ luôn có ít nhất một phần tử:

let x::_ = myList 

Trong gia đình này của ngôn ngữ, đó là hầu như luôn luôn chỉ ra một lỗ hổng thiết kế. Giải pháp đúng là đại diện cho danh sách không trống của bạn bằng cách sử dụng một loại mà làm cho nó không thể đại diện cho danh sách trống. Kiểm tra kiểu tĩnh sau đó chứng minh rằng danh sách của bạn không thể trống và, do đó, đảm bảo rằng nguồn lỗi thời gian chạy này đã được loại bỏ hoàn toàn khỏi mã của bạn.

Ví dụ: bạn có thể đại diện cho danh sách không trống dưới dạng bộ tảo chứa đầu và danh sách đuôi. Khớp mẫu của bạn sau đó sẽ trở thành:

let x, _ = myList 

Điều này là đầy đủ để trình biên dịch hài lòng và không đưa ra cảnh báo. Mã này không thể hoạt động sai tại thời gian chạy.

Tôi đã trở thành người ủng hộ kỹ thuật này vào năm 2004, khi tôi tái cấu trúc khoảng 1kLOC mã OCaml thương mại đã là nguồn chính của lỗi thời gian chạy trong ứng dụng (mặc dù chúng rõ ràng ở dạng bắt tất cả các trường hợp trùng khớp đã đưa ra ngoại lệ). Việc tái cấu trúc của tôi đã xóa tất cả các nguồn lỗi thời gian chạy khỏi hầu hết mã. Độ tin cậy của toàn bộ ứng dụng được cải thiện rất nhiều. Hơn nữa, chúng tôi đã lãng phí hàng tuần để tìm lỗi thông qua gỡ lỗi nhưng việc tái cấu trúc của tôi đã hoàn thành trong vòng 2 ngày. Vì vậy, kỹ thuật này thực sự trả cổ tức trong thế giới thực.

+2

Nitpick: nếu hành vi của mã đã thay đổi, bạn không được tái cấu trúc. – Douglas

+2

@Jon: bạn bị kẹt trong vấn đề Python, tôi hiểu. Xem nhận xét của tôi với JSBands trong câu trả lời của Justin Niessner vì sao triết lý Erlang không quá tệ. Erlang có nhiều ứng dụng ổn định hơn trong viễn thông hơn bất cứ thứ gì. Net đã đạt được cho đến nay. Cố gắng hiểu mặt khác của câu hỏi trước khi trả lời. Hãy để nó sụp đổ dịch trong Erlang để cho một số quá trình khác sửa lỗi. Tôi giới thiệu bạn đến [Luận án của Joe Armstrong] (http://www.erlang.org/download/armstrong_thesis_2003.pdf) để biết thêm chi tiết. –

+2

Hài hước, "Nguồn cấp dữ liệu về lỗi thời gian chạy" là chính xác tại sao Erlang không ép buộc bạn thêm những thứ như '| _ ->() 'chỉ để loại bỏ chúng. Erlang sẽ luôn trả về 'badmatch' nếu bạn không khớp với một trong các trường hợp đã cho. –

3

OCaml theo mặc định sẽ cảnh báo bạn về các trận đấu không hoàn chỉnh. Bạn có thể tắt nó bằng cách thêm "p" vào cờ "-w". Ý tưởng với những cảnh báo này là thường xuyên hơn không (ít nhất là trong kinh nghiệm của tôi) họ là một dấu hiệu của lỗi lập trình viên. Đặc biệt là khi tất cả các mẫu của bạn thực sự phức tạp như Node (Node (Leaf 4) x) (Node y (Node Empty _)), rất dễ bỏ lỡ một trường hợp. Khi lập trình viên chắc chắn rằng nó không thể đi sai, rõ ràng thêm một trường hợp | _ -> assert false là một cách rõ ràng để chỉ ra điều đó.

GHC theo mặc định sẽ tắt các cảnh báo này; nhưng bạn có thể bật tính năng này với -fwarn-incomplete-patterns

3

Erlang không thể có khớp mẫu đầy đủ vì các loại động trừ khi bạn có tính năng nắm bắt mọi thứ, điều đó thật ngớ ngẩn. Ocaml, mặt khác, có thể. Ocaml cũng cố gắng đẩy tất cả các vấn đề có thể bị bắt vào thời gian biên dịch để biên dịch.

+0

Vấn đề nhập động là quan trọng. Cảm ơn. –

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