Tại sao có nhiều hàm `map` cho các loại khác nhau trong F #


9

Tôi đang học F #. Tôi đã bắt đầu FP với Haskell và tôi đã tò mò về điều này.

Vì F # là ngôn ngữ .NET, nên có vẻ hợp lý hơn đối với tôi khi khai báo giao diện như Mappable, giống như Functorlớp loại haskell .

nhập mô tả hình ảnh ở đây

Nhưng giống như hình trên, các hàm F # được tách biệt và tự thực hiện. Mục đích thiết kế của thiết kế như vậy là gì? Đối với tôi, việc giới thiệu Mappable.mapvà thực hiện điều này cho từng loại dữ liệu sẽ thuận tiện hơn.


Câu hỏi này không thuộc về SO. Nó không phải là một vấn đề lập trình. Tôi đề nghị bạn hỏi trong F # Slack hoặc một số diễn đàn thảo luận khác.
Bent Tranberg

5
@BentTranberg Đọc rộng rãi, The community is here to help you with specific coding, algorithm, or language problems.cũng sẽ bao gồm các câu hỏi thiết kế ngôn ngữ, miễn là các tiêu chí khác được đáp ứng.
kaefer

3
Câu chuyện dài, F # không có các lớp loại, và do đó cần phải thực hiện lại mapvà chức năng bậc cao phổ biến khác cho mỗi loại bộ sưu tập. Một giao diện sẽ giúp ích rất ít vì nó vẫn yêu cầu mỗi loại bộ sưu tập cung cấp một triển khai riêng.
dumetrulo

Câu trả lời:


19

Vâng, một câu hỏi rất đơn giản trên bề mặt. Nhưng nếu bạn dành thời gian để suy nghĩ cho đến cuối cùng, bạn sẽ đi sâu vào lý thuyết loại vô cùng. Và loại lý thuyết cũng nhìn chằm chằm vào bạn.

Tất nhiên, trước tiên, bạn đã nhận ra một cách chính xác rằng F # không có các lớp loại và đó là lý do tại sao. Nhưng bạn đề xuất một giao diện Mappable. Ok, chúng ta hãy nhìn vào đó.

Hãy nói rằng chúng ta có thể tuyên bố một giao diện như vậy. Bạn có thể tưởng tượng chữ ký của nó sẽ trông như thế nào không?

type Mappable =
    abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>

Đâu flà kiểu thực hiện giao diện. Đợi đã! F # cũng không có điều đó! Đây flà một biến loại cao hơn và F # hoàn toàn không có loại tốt hơn. Không có cách nào để khai báo một chức năng f : 'm<'a> -> 'm<'b>hoặc một cái gì đó như thế.

Nhưng ok, giả sử chúng ta cũng vượt qua rào cản đó. Và bây giờ chúng ta có một giao diện Mappablecó thể được thực hiện bởi List, Array, Seq, và các bồn rửa chén. Nhưng chờ đã! Bây giờ chúng ta có một phương thức thay vì một hàm và các phương thức không được soạn thảo tốt! Hãy xem xét thêm 42 vào mọi thành phần của danh sách lồng nhau:

// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))

// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))

Hãy nhìn: bây giờ chúng ta phải sử dụng một biểu thức lambda! Không có cách nào để chuyển việc .mapthực hiện này sang một chức năng khác như một giá trị. Thực tế, kết thúc "chức năng là giá trị" (và vâng, tôi biết, sử dụng lambda trông không tệ lắm trong ví dụ này, nhưng tin tôi đi, nó trở nên rất xấu xí)

Nhưng chờ đã, chúng ta vẫn chưa xong. Bây giờ, đó là một cuộc gọi phương thức, gõ suy luận không hoạt động! Bởi vì chữ ký loại của phương thức .NET phụ thuộc vào loại đối tượng, không có cách nào để trình biên dịch suy ra cả hai. Đây thực sự là một vấn đề rất phổ biến người mới gặp phải khi tương tác với các thư viện .NET. Và cách chữa trị duy nhất là cung cấp một chữ ký loại:

add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))

Ồ, nhưng điều này vẫn chưa đủ! Mặc dù tôi đã cung cấp một chữ ký cho nestedListchính nó, tôi đã không cung cấp một chữ ký cho tham số của lambda l. Chữ ký như vậy nên là gì? Bạn có thể nói nó nên được fun (l: #Mappable) -> ...không? Ồ, và bây giờ cuối cùng chúng ta cũng đã đạt được loại N, như bạn thấy, #Mappablelà một lối tắt cho "bất kỳ loại nào 'anhư vậy 'a :> Mappable" - tức là một biểu thức lambda tự nó là chung chung.

Hoặc, thay vào đó, chúng ta có thể quay trở lại loại tốt hơn và tuyên bố loại nestedListchính xác hơn:

add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...

Nhưng ok, bây giờ chúng ta hãy bỏ qua suy luận kiểu và quay lại biểu thức lambda và làm thế nào bây giờ chúng ta không thể chuyển mapnhư một giá trị cho hàm khác. Giả sử chúng ta mở rộng cú pháp một chút để cho phép một cái gì đó giống như những gì Elm làm với các trường bản ghi:

add42 nestedList = nestedList.map (.map ((+) 42))

Loại .mapsẽ là gì? Nó sẽ phải là một loại hạn chế , giống như trong Haskell!

.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>

Oh, tốt. Đặt sang một bên thực tế là .NET thậm chí không cho phép các loại như vậy tồn tại, thực sự chúng ta vừa lấy lại các lớp loại!

Nhưng có một lý do mà F # không có lớp loại ở vị trí đầu tiên. Nhiều khía cạnh của lý do đó được mô tả ở trên, nhưng một cách ngắn gọn hơn để đặt nó là: sự đơn giản .

Để bạn thấy, đây là một quả bóng sợi. Khi bạn có các lớp loại, bạn phải có các ràng buộc, loại cao hơn, hạng N (hoặc ít nhất là hạng 2) và trước khi bạn biết điều đó, bạn đang yêu cầu các kiểu bắt buộc, hàm loại, GADT và tất cả phần còn lại của nó

Nhưng Haskell phải trả giá cho tất cả các goodies. Hóa ra không có cách nào tốt để suy ra tất cả những thứ đó. Các loại sắp xếp cao hơn hoạt động, nhưng các ràng buộc đã không được. Hạng-N - thậm chí không mơ về nó. Và ngay cả khi nó hoạt động, bạn nhận được các lỗi loại mà bạn phải có bằng tiến sĩ để hiểu. Và đó là lý do tại Haskell, bạn được khuyến khích nhẹ nhàng đặt chữ ký loại lên mọi thứ. Chà, không phải tất cả mọi thứ - mọi thứ , nhưng thực sự là hầu hết mọi thứ. Và nơi bạn không đặt chữ ký loại (ví dụ: bên trong letwhere) - ngạc nhiên - ngạc nhiên, những địa điểm đó thực sự bị biến dạng, do đó, về cơ bản bạn trở lại trong F # -land đơn giản.

Mặt khác, trong F #, chữ ký loại rất hiếm, chủ yếu chỉ dành cho tài liệu hoặc cho .NET interop. Ngoài hai trường hợp đó, bạn có thể viết cả một chương trình phức tạp lớn trong F # và không sử dụng chữ ký loại một lần. Kiểu suy luận hoạt động tốt, bởi vì không có gì quá phức tạp hoặc mơ hồ để nó xử lý.

Và đây là lợi thế lớn của F # so với Haskell. Vâng, Haskell cho phép bạn thể hiện những thứ siêu phức tạp theo một cách rất chính xác, điều đó thật tốt. Nhưng F # cho phép bạn rất mơ mộng, gần giống như Python hoặc Ruby và vẫn có trình biên dịch bắt bạn nếu bạn vấp ngã.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.