2013-04-05 39 views
12

Trong OCaml, nó là hợp pháp phải có trong .mli:η-mở rộng bằng một ngôn ngữ chức năng thuần túy

val f : 'a -> 'a 
val g : 'a -> 'a 

.ml:

let f x = x 
let g = f 

Tuy nhiên, trong F #, điều này bị từ chối:

eta_expand.ml(2,5): error FS0034: Module 'Eta_expand' contains 
    val g : ('a -> 'a)  
but its signature specifies 
    val g : 'a -> 'a  

The arities in the signature and implementation differ. The signature specifies that 'g' is function definition or lambda expression accepting at least 1 argument(s), but the implementation is a computed function value. To declare that a computed function value is a permitted implementation simply parenthesize its type in the signature, e.g. 
    val g: int -> (int -> int) 
instead of 
    val g: int -> int -> int. 

Một giải pháp thay thế là η mở rộng định nghĩa của g:

let g x = f x 

Nếu mã của tôi là hoàn toàn chức năng (không có ngoại lệ, không có tác dụng phụ, vv) này cần phải tương ứng (trên thực tế, nó có thể là tốt hơn đối với đa hình với, tùy thuộc vào cách ngôn ngữ khái quát các loại: trong OCaml các ứng dụng một phần không tạo ra các chức năng đa hình, nhưng các η-mở rộng của chúng không).

Có bất kỳ hạn chế nào đối với việc mở rộng system-hệ thống không?

Hai câu trả lời gợi ý câu hỏi về η-mở rộng :-) và thay vào đó đề nghị tôi thêm dấu ngoặc đơn quanh loại chức năng của mình. Điều này là do, rõ ràng, F # phân biệt ở mức đánh máy giữa định nghĩa "đúng" của các hàm (như các biểu thức and và các định nghĩa được tính toán, như trong các ứng dụng một phần); có lẽ điều này là do các biểu thức directly trực tiếp ánh xạ tới các hàm CLR trong khi các định nghĩa được tính toán ánh xạ tới các đối tượng đại biểu. (Tôi không chắc chắn về cách giải thích này và sẽ đánh giá cao nếu ai đó rất quen thuộc với F # có thể chỉ để tham khảo tài liệu mô tả này.)

Một giải pháp sẽ có thêm hệ thống dấu ngoặc đơn để tất cả các loại chức năng trong .mli, nhưng Tôi lo sợ điều này có thể dẫn đến sự thiếu hiệu quả. Khác sẽ là để phát hiện các chức năng tính toán và thêm dấu ngoặc đơn các loại tương ứng trong .mli. Một giải pháp thứ ba sẽ là η-mở rộng các trường hợp rõ ràng, và ngoặc đơn những người khác.

Tôi không đủ quen thuộc với nội dung F #/CLR để đo lường nội dung nào có hiệu suất đáng kể hoặc giao tiếp hình phạt.

+2

Chỉ cần làm cho nó 'val g: ('a ->' a)'. Nó là một tính năng/lỗi đã biết của hệ thống kiểu F #. –

+0

Một lần nữa, "chỉ cần làm cho nó" - có lẽ nếu λ biểu thức và chức năng tính toán không có các loại hoán đổi cho nhau, đó là lý do chính đáng. Hơn nữa, đây là mã được tạo tự động, do đó vấn đề phức tạp hơn một chút so với việc thêm một vài dấu ngoặc đơn bằng tay ... –

+0

Có sự khác biệt giữa hai trường hợp này, một được biên dịch thành một phương thức và phương thức kia thành một giá trị tĩnh. Func 'thuộc tính. Tính tương thích không phải là hai chiều (nghĩa là, 'a -> b: <: (a -> b)', nhưng không phải là '(a -> b): <: a -> b'). –

Trả lời

4

Điều thú vị là tôi fsi đưa ra một thông báo lỗi hữu ích hơn:

/test.fs(2,5): error FS0034: Module 'Test' contains 
    val g : ('a -> 'a) but its signature specifies 
    val g : 'a -> 'a The arities in the signature and implementation differ. 
      The signature specifies that 'g' is function definition or lambda expression 
      accepting at least 1 argument(s), but the implementation is a computed 
      function value. To declare that a computed function value is a permitted 
      implementation simply parenthesize its type in the signature, e.g. 
     val g: int -> (int -> int) instead of 
     val g: int -> int -> int. 

Nếu bạn thêm dấu ngoặc để có được g :('a -> 'a) tất cả là tốt

+1

Tôi đã cắt phần cuối của thông báo lỗi vì lợi ích ngắn gọn. Tôi không đồng ý với "tất cả là tốt". :-) –

8

Về lý thuyết, F # chức năng loại 'a -> 'b -> 'c là loại giống như 'a -> ('b -> 'c). Có nghĩa là, nhiều hàm đối số được biểu diễn bằng cách sử dụng biểu mẫu được cuộn trong F #. Bạn có thể sử dụng vị trí mà người kia mong đợi trong hầu hết các trường hợp, ví dụ: khi gọi hàm bậc cao hơn.

Tuy nhiên, vì lý do thực tế, trình biên dịch F # thực sự phân biệt giữa các loại - động lực là chúng được trình bày khác nhau trong mã .NET được biên dịch. Điều này có tác động đến hiệu suất và khả năng tương tác với C#, do đó, nó rất hữu ích để làm cho sự khác biệt đó.

Một chức năng Foo : int -> int -> int sẽ được biên dịch thành một thành viên int Foo(int, int) - trình biên dịch không sử dụng hình thức curried theo mặc định, vì điều này hiệu quả hơn khi gọi Foo với cả hai đối số (trường hợp phổ biến hơn) và tốt hơn cho interop .Một hàm Bar : int -> (int -> int) sẽ được biên dịch là FSharpFunc<int, int> Bar(int) - thực sự sử dụng biểu mẫu đã được curried (và vì vậy nó hiệu quả hơn để gọi nó chỉ với một tham số duy nhất và sẽ khó sử dụng từ C#).

Đây cũng là lý do tại sao F # không xử lý các loại như nhau khi nói đến chữ ký - chữ ký chỉ định loại, nhưng ở đây nó cũng xác định hàm sẽ được biên dịch như thế nào. Tệp triển khai phải cung cấp chức năng đúng loại, nhưng - trong trường hợp này - cũng có dạng được biên dịch đúng.

+0

Tôi hiểu sự khác biệt. Những gì tôi không biết là giải pháp tốt nhất: tôi nên (a) η-mở rộng tất cả các chức năng tính toán (b) ngoặc đơn các loại của tất cả các chức năng tính toán? –

+0

@monniaux η-mở rộng tất cả các chức năng được tính toán sẽ là lựa chọn mặc định của tôi. Nhưng nó phụ thuộc vào một số yếu tố. Nếu bạn muốn khả năng tương tác của C# thì chắc chắn (a), nếu bạn thường sử dụng một phần ứng dụng trên các hàm được tính toán thì (b) sẽ cho bạn hiệu năng tốt hơn. –

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