2010-07-01 59 views
65

Trình biên dịch F # xuất hiện để thực hiện suy luận kiểu trong một (khá) nghiêm ngặt từ trên xuống dưới, từ trái sang phải. Điều này có nghĩa là bạn phải làm mọi thứ như đặt tất cả các định nghĩa trước khi sử dụng, thứ tự biên dịch tệp là quan trọng và bạn có xu hướng cần sắp xếp lại nội dung (qua |> hoặc bạn có gì) để tránh có chú thích loại rõ ràng.Tại sao suy luận kiểu của F # lại thay đổi?

Làm cách nào để nó linh hoạt hơn và được lên kế hoạch cho phiên bản F # trong tương lai? Rõ ràng nó có thể được thực hiện, kể từ Haskell (ví dụ) không có những hạn chế như vậy với suy luận mạnh mẽ như nhau. Có điều gì khác biệt về thiết kế hay ý thức hệ của F # đang gây ra điều này?

+3

Tôi thực sự không thích câu hỏi này, nhưng nó đã ghi lại một số câu trả lời tuyệt vời và được chiếu sáng, vì vậy tôi cũng rất thích thú :) – Brian

+7

@J Cooper: "Haskell (ví dụ) không có giới hạn như vậy với suy luận mạnh mẽ". Haskell là hư không gần như có suy luận loại mạnh mẽ khi bạn xem xét các tạp chất hoặc hiệu suất. Ví dụ, chức năng 'tầng' của Haskell thường chạy các đơn đặt hàng có cường độ chậm hơn bất kỳ ngôn ngữ được biên dịch nào khác chính xác vì không thể suy ra loại tĩnh chính xác để nó hoạt động theo thời gian chạy. Ngoài ra, nếu tôi ngừng loại bỏ chú thích loại cấp cao nhất khỏi một hàm 'randIntList', tôi có ở đây thì nó sẽ ngừng biên dịch với lỗi' biến dạng mơ hồ '. –

+7

Tôi thích câu hỏi bởi vì tôi cho rằng hầu như tất cả những người mới bắt đầu học F # đều có hai ý nghĩ: "WOW, F # thật mạnh mẽ!" và "WTF, tại sao F # không thể suy luận ngớ ngẩn này ?!" :) –

Trả lời

49

Về "suy luận mạnh mẽ của Haskell", tôi không nghĩ Haskell có để đối phó với

  • OO kiểu subtyping động (các lớp học kiểu có thể làm một số công cụ tương tự, nhưng các lớp học kiểu dễ gõ/suy luận)
  • phương pháp quá tải (các lớp học kiểu có thể làm một số công cụ tương tự, nhưng loại lớp dễ dàng nhập/suy luận hơn)

Tức là, tôi nghĩ F # phải đối phó với một số thứ khó mà Haskell không làm. (Gần như chắc chắn, Haskell phải đối phó với một số thứ khó mà F # không.Như được đề cập bởi các câu trả lời khác, hầu hết các ngôn ngữ chính của .NET đều có công cụ Visual Studio như là một ảnh hưởng lớn về thiết kế ngôn ngữ (xem ví dụ như LINQ có "từ ... chọn" thay vì SQL-y "như thế nào?" chọn ... từ ", được thúc đẩy bằng cách lấy intellisense từ tiền tố chương trình). Intellisense, error squiggles, và error-message comprehensibility là tất cả các yếu tố công cụ thông báo cho thiết kế F #. Bạn có thể làm tốt hơn và phỏng đoán thêm (mà không phải hy sinh những trải nghiệm khác), nhưng tôi không nghĩ rằng đó là một trong những ưu tiên cao của chúng tôi đối với các phiên bản ngôn ngữ trong tương lai. (Các Haskellers có thể thấy suy luận kiểu F # là hơi yếu, nhưng chúng có lẽ cao hơn nhiều so với C# ers, những người thấy suy luận kiểu F # rất mạnh. :))

Cũng có thể khó mở rộng suy luận kiểu trong một không phá vỡ thời trang; có thể thay đổi chương trình bất hợp pháp thành chương trình hợp pháp trong phiên bản tương lai, nhưng bạn phải rất cẩn thận để đảm bảo các chương trình pháp lý trước đó không thay đổi ngữ nghĩa theo quy tắc suy luận mới và độ phân giải tên (một cơn ác mộng khủng khiếp ở mọi ngôn ngữ) để tương tác với các thay đổi kiểu suy luận theo những cách đáng ngạc nhiên.

+1

Xem thêm http://lorgonblog.wordpress.com/2009/10/25/overview-of-type-inference-in-f/ – Brian

+1

Oh không có phương pháp quá tải một chút dễ dàng hơn cho Haskell. Định nghĩa chức năng (tham số và kiểu trả về) được giải mã một cách rõ ràng .. – nawfal

16

F # sử dụng một đường chuyền biên soạn như vậy bạn chỉ có thể tham khảo các loại hoặc chức năng mà đã được xác định hoặc trước đó trong file bạn hiện tại hoặc xuất hiện trong một tập tin mà được quy định trước đó trong thứ tự biên dịch.

Gần đây tôi đã hỏi Don Syme về việc thực hiện nhiều nguồn chuyển để cải thiện quy trình suy luận loại . trả lời của ông là "Vâng, nó có thể làm đa-pass loại suy luận. Ngoài ra còn có single-pass biến thể tạo ra một tập hợp hữu hạn những hạn chế.

Tuy nhiên những phương pháp tiếp cận có xu hướng cung cấp cho thông báo lỗi xấu và nghèo kết quả intellisense trong trình chỉnh sửa trực quan . "

http://www.markhneedham.com/blog/2009/05/02/f-stuff-i-get-confused-about/#comment-16153

+2

Thú vị. Tôi dường như không nhận được trợ giúp intellisense tuyệt vời cho đồng bằng chức năng F # anyway; chủ yếu là nó dường như đi vào chơi khi bạn làm Module.somefunc hoặc sử dụng một lớp (mà về cơ bản giết chết suy luận kiểu anyway). Tôi tự hỏi làm thế nào thiết lập trong đá thiết kế này là sự lựa chọn. –

+1

Có vẻ như bạn không tạo dự án VS cho mã của mình hoặc bạn đang để lại các lỗi cú pháp trong mã của mình. Miễn là bạn không làm điều này, intellisense là một trợ giúp lớn và sẽ báo cáo các loại của mỗi biến trên chuột di chuột, cộng với ngay lập tức chỉ ra lỗi loại. Khi tôi lập trình trong F #, điều này làm cho tôi rất nhanh nhẹn - đó là một tính năng cực kỳ quan trọng, và chi phí chỉ là sử dụng hiếm hoi của |> và rất hiếm khi loại ràng buộc. – RD1

18

Tôi nghĩ rằng các thuật toán được sử dụng bởi F # có những lợi ích mà nó rất dễ dàng để (ít nhất là xấp xỉ) giải thích cách thức hoạt động, do đó khi bạn hiểu nó, bạn có thể có một số dự đoán về kết quả.

Thuật toán sẽ luôn có một số hạn chế. Hiện tại, khá dễ hiểu chúng. Đối với các thuật toán phức tạp hơn, điều này có thể khó khăn. Ví dụ: tôi nghĩ bạn có thể gặp phải tình huống mà bạn cho rằng thuật toán có thể suy ra điều gì đó - nhưng nếu nó đủ chung để bao trùm trường hợp, nó sẽ không thể giải mã được (ví dụ: có thể lặp lại mãi mãi).

Một ý nghĩ khác là kiểm tra mã từ trên xuống dưới tương ứng với cách chúng ta đọc mã (ít nhất là đôi khi). Vì vậy, có thể là thực tế là chúng tôi có xu hướng viết mã theo cách cho phép loại suy luận cũng làm cho mã dễ đọc hơn đối với ...

+9

Trên điểm cuối cùng của bạn, và tôi có thể kỳ lạ theo cách này, tôi có xu hướng nhóm các chức năng liên quan theo cách ngược lại (bằng các ngôn ngữ cho phép tôi). Ví dụ, nói rằng tôi có hàm C phức tạp, và hàm A và B đều sử dụng hàm C, sau đó tôi đặt các hàm của tôi từ trên xuống dưới A, B, C. Tôi cho rằng tôi thích đọc nó theo cách đó vì cách thực hiện unravels (cho tôi xem bức tranh lớn, rồi xem chi tiết). Điều đó đang được nói, tôi đã không quan tâm đến các lực lượng F # đặt hàng quá nhiều, mặc dù tôi đã không làm việc trên các dự án lớn với nó. –

+3

Thứ tự bạn thích là tự nhiên nhất với gọi theo tên bởi vì nó phản ánh thứ tự đánh giá. Với cuộc gọi theo giá trị, cách F # tự nhiên hơn - đặc biệt vì các định nghĩa có thể có hiệu ứng tức thời. – RD1

51

Có điều gì khác biệt về thiết kế hoặc ý thức hệ của F # đang gây ra điều này không?

Có. F # sử dụng danh nghĩa chứ không phải là gõ cấu trúc vì nó đơn giản hơn và do đó, dễ dàng hơn cho các con người chỉ sử dụng.

Xem xét việc này F # ví dụ:

let lengths (xss: _ [] []) = Array.map (fun xs -> xs.Length) xss 

let lengths (xss: _ [] []) = xss |> Array.map (fun xs -> xs.Length) 

Các cựu không biên dịch vì loại xs bên trong hàm ẩn danh không thể được suy ra vì F # không thể diễn tả kiểu "một số lớp học với thành viên Length".

Ngược lại, OCaml thể diễn tả tương đương trực tiếp:

let lengths xss = Array.map (fun xs -> xs#length) xss 

vì OCaml thể diễn tả kiểu đó (nó được viết <length: 'a ..>). Lưu ý rằng điều này đòi hỏi suy luận kiểu mạnh mẽ hơn F # hoặc Haskell hiện có, ví dụ: OCaml có thể suy ra các loại tổng hợp.

Tuy nhiên, tính năng này được biết là một vấn đề về khả năng sử dụng. Ví dụ, nếu bạn vít lên ở nơi khác trong mã thì trình biên dịch vẫn chưa suy ra rằng loại xs được cho là một mảng vì vậy bất kỳ thông báo lỗi nào mà nó có thể cung cấp chỉ có thể cung cấp thông tin như "một số loại có độ dài" và không phải là "một mảng". Chỉ với mã hơi phức tạp hơn, điều này nhanh chóng mất kiểm soát khi bạn có các loại lớn với nhiều thành viên suy luận cấu trúc không hoàn toàn thống nhất, dẫn đến các thông báo lỗi không thể hiểu được (C++/STL).

+4

Thú vị. Tất cả những điều được xem xét, bạn có thích đánh máy danh nghĩa của F # hoặc gõ cấu trúc của OCaml? –

+13

Học thuật trong tôi thích cách OCaml hơn vì nó có thể ngắn gọn hơn nhiều (ví dụ: 'System.Windows.Controls.ScrollBarVisibility.Visible' trên .NET chỉ là' Hiển thị' trong OCaml). Các doanh nhân trong tôi thích F # cách vì dễ sử dụng cho khách hàng của tôi là điều cần thiết cho khả năng tồn tại thương mại. Có lẽ có một sự thỏa hiệp tốt hơn mặc dù. –

12

Câu trả lời ngắn gọn là F # được dựa trên truyền thống của SML và OCaml, trong khi Haskell xuất phát từ một thế giới hơi khác biệt của Miranda, Gofer và tương tự. Sự khác biệt trong truyền thống lịch sử là tinh tế, nhưng phổ biến. Sự khác biệt này song song với các ngôn ngữ hiện đại khác, chẳng hạn như Coq giống như ML, có cùng giới hạn đặt hàng so với Agda giống như Haskell mà không có.

Sự khác biệt này có liên quan đến đánh giá chậm và nghiêm ngặt. Phía Haskell của vũ trụ tin vào sự lười biếng, và một khi bạn đã tin vào sự lười biếng, ý tưởng thêm sự lười biếng vào những thứ như suy luận kiểu là không có trí tuệ.Trong khi ở phía ML của vũ trụ bất cứ khi nào sự lười biếng hoặc đệ quy lẫn nhau là cần thiết, nó phải được ghi chú rõ ràng bằng cách sử dụng các từ khóa như với, , rec, v.v. mã soạn sẵn, nhưng có rất nhiều người nghĩ rằng tốt hơn là làm cho những điều này rõ ràng.

+1

@ng: "... có rất nhiều người nghĩ tốt hơn là làm cho những điều này rõ ràng". Rất nhiều người sẽ chú thích rõ ràng sự lười biếng hơn là sự khắt khe và tạp chất vì lười biếng rất hiếm khi hữu ích trong khi độ nghiêm ngặt và tạp chất có mặt khắp nơi (ngay cả trong mã Haskell thực). –

+5

@JonHarrop Đúng vậy. Nhưng nó thay đổi ngữ nghĩa của mã. Rất nhiều người sẽ tranh luận với bạn rằng sự lười biếng là rất hữu ích rất thường xuyên và cho phép các cách tiếp cận khác nhau để giải quyết một vấn đề. Nó khá rõ ràng rằng khả năng làm cả hai là tốt nhất, mà Haskell thực sự cung cấp, mặc dù thiên vị đối với phía lười biếng thay vì bên nghiêm ngặt như hầu hết các ngôn ngữ. –

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