Thành phần Haskell (.) So với toán tử chuyển tiếp ống của F # (|>)


100

Trong F #, việc sử dụng toán tử chuyển tiếp ống dẫn |>, khá phổ biến. Tuy nhiên, trong Haskell, tôi chỉ từng thấy thành phần hàm (.), đang được sử dụng. Tôi hiểu rằng chúng có liên quan với nhau , nhưng có lý do ngôn ngữ nào khiến tính năng chuyển tiếp qua không được sử dụng trong Haskell hay là thứ gì khác?


2
câu trả lời lihanseys nói rằng đó &là của Haskell |>. Chôn sâu trong chủ đề này và tôi mất vài ngày để khám phá. Tôi sử dụng nó rất nhiều, bởi vì bạn tự nhiên đọc từ trái sang phải để làm theo mã của bạn.
itmuckel

Câu trả lời:


61

Tôi đang suy đoán một chút ...

Văn hóa : Tôi nghĩ |>là một nhà điều hành quan trọng trong "văn hóa" F #, và có lẽ tương tự với .Haskell. F # có một toán tử thành phần hàm <<nhưng tôi nghĩ cộng đồng F # có xu hướng sử dụng kiểu không tính điểm ít hơn cộng đồng Haskell.

Sự khác biệt về ngôn ngữ : Tôi không biết đủ về cả hai ngôn ngữ để so sánh, nhưng có lẽ các quy tắc để khái quát hóa các ràng buộc chữ là đủ khác nhau để ảnh hưởng đến điều này. Ví dụ, tôi biết trong F # đôi khi viết

let f = exp

sẽ không biên dịch và bạn cần chuyển đổi eta rõ ràng:

let f x = (exp) x   // or x |> exp

để làm cho nó biên dịch. Điều này cũng hướng mọi người khỏi phong cách không điểm / không có thành phần và hướng tới phong cách pipelining. Ngoài ra, suy luận kiểu F # đôi khi yêu cầu pipelining, để một kiểu đã biết xuất hiện ở bên trái (xem tại đây ).

(Cá nhân tôi thấy văn phong không có điểm không thể đọc được, nhưng tôi cho rằng mọi thứ mới / khác nhau dường như không thể đọc được cho đến khi bạn quen với nó.)

Tôi nghĩ rằng cả hai đều có khả năng tồn tại ở một trong hai ngôn ngữ và lịch sử / văn hóa / tai nạn có thể xác định lý do tại sao mỗi cộng đồng lại định cư tại một "điểm thu hút" khác nhau.


7
Tôi đồng ý với sự khác biệt văn hóa. Haskell truyền thống sử dụng .$, vì vậy mọi người tiếp tục sử dụng chúng.
Amok 21/09/09

9
Point free đôi khi dễ đọc hơn pointful, đôi khi ít hơn. Tôi thường sử dụng nó trong đối số cho các chức năng như bản đồ và bộ lọc, để tránh việc lambda làm lộn xộn mọi thứ. Đôi khi tôi cũng sử dụng nó trong các chức năng cấp cao nhất, nhưng ít thường xuyên hơn và chỉ khi nó đơn giản.
Paul Johnson

2
Tôi không thấy nhiều văn hóa trong đó, theo nghĩa đơn giản là không có nhiều lựa chọn trong vấn đề liên quan đến F # (vì những lý do mà bạn và Ganesh đề cập). Vì vậy, tôi muốn nói rằng cả hai đều khả thi trong Haskell, nhưng F # chắc chắn được trang bị tốt hơn nhiều để sử dụng nhà điều hành đường ống.
Kurt Schelfthout

2
vâng, văn hóa đọc từ trái sang phải :) ngay cả toán học cũng nên được dạy theo cách đó ...
nicolas

1
@nicolas từ trái sang phải là một lựa chọn tùy ý. Bạn có thể làm quen với từ phải sang trái.
Martin Capodici

86

Trong F # (|>)là quan trọng vì cách đánh máy từ trái sang phải. Ví dụ:

List.map (fun x -> x.Value) xs

nói chung sẽ không đánh máy, bởi vì ngay cả khi loại của xsđược biết, loại đối số xcho lambda không được biết vào thời điểm người đánh máy nhìn thấy nó, vì vậy nó không biết cách giải quyết x.Value.

Ngược lại

xs |> List.map (fun x -> x.Value)

sẽ hoạt động tốt, bởi vì loại xsý chí dẫn đến loại xđược biết đến.

Việc phân loại từ trái sang phải là bắt buộc vì việc phân giải tên liên quan đến các cấu trúc như x.Value. Simon Peyton Jones đã viết một đề xuất về việc thêm một kiểu phân giải tên tương tự cho Haskell, nhưng thay vào đó, ông đề xuất sử dụng các ràng buộc cục bộ để theo dõi xem một kiểu có hỗ trợ một thao tác cụ thể hay không. Vì vậy, trong mẫu đầu tiên, yêu cầu xcần một thuộc Valuetính sẽ được chuyển tiếp cho đến khi xsđược nhìn thấy và yêu cầu này có thể được giải quyết. Tuy nhiên, điều này làm phức tạp hệ thống kiểu.


1
Điều thú vị là có (<|) toán tử tương tự như (.) Trong Haskell với cùng hướng dữ liệu từ phải sang trái. Nhưng làm thế nào sẽ làm việc phân giải loại cho nó?
The_Ghost 24/09/09

7
(<|) thực sự tương tự như Haskell's ($). Việc đánh máy từ trái sang phải chỉ được yêu cầu để giải quyết những thứ như .Value, vì vậy (<|) hoạt động tốt trong các trường hợp khác hoặc nếu bạn sử dụng chú thích kiểu rõ ràng.
GS - Xin lỗi Monica

44

Thêm suy đoán, lần này từ phía Haskell chủ yếu ...

($)là sự thay đổi (|>)và việc sử dụng nó khá phổ biến khi bạn không thể viết mã không có điểm. Vì vậy, lý do chính (|>)không được sử dụng trong Haskell là vị trí của nó đã bị chiếm đoạt ($).

Ngoài ra, nói từ một chút kinh nghiệm về F #, tôi nghĩ (|>)nó rất phổ biến trong mã F # vì nó giống với Subject.Verb(Object)cấu trúc của OO. Vì F # hướng đến sự tích hợp chức năng / OO trơn tru, đây Subject |> Verb Objectlà một bước chuyển đổi khá suôn sẻ cho các lập trình viên chức năng mới.

Cá nhân tôi cũng thích suy nghĩ từ trái sang phải, vì vậy tôi sử dụng (|>)Haskell, nhưng tôi không nghĩ nhiều người khác làm như vậy.


Xin chào Nathan, "flip ($)" có được xác định trước ở bất kỳ đâu trong nền tảng Haskell không? Tên "(|>)" đã được định nghĩa trong Data.Sequence với một nghĩa khác. Nếu chưa được xác định, bạn gọi nó là gì? Tôi đang nghĩ đến việc sử dụng "($>) = flip ($)"
mattbh

2
@mattbh: Tôi không thể tìm thấy bằng Hoogle. Tôi không biết về điều đó Data.Sequence.|>, nhưng có $>vẻ hợp lý để tránh xung đột ở đó. Thành thật mà nói, chỉ có rất nhiều nhà điều hành tốt, vì vậy tôi sẽ chỉ sử dụng |>cho cả hai và quản lý xung đột trên cơ sở từng trường hợp. (Ngoài ra tôi sẽ bị cám dỗ để chỉ bí danh Data.Sequence.|>như snoc)
Nathan Shively-Sanders

2
($)(|>)là ứng dụng không phải là thành phần. Cả hai có liên quan (như câu hỏi ghi chú) nhưng chúng không giống nhau (của bạn fc(Control.Arrow.>>>)cho các chức năng).
Nathan Shively-Sanders

11
Trên thực tế, F # |>thực sự khiến tôi nhớ đến UNIX |hơn bất cứ thứ gì khác.
Kevin Cantu

3
Một lợi ích khác của |>F # là nó có các thuộc tính tốt cho IntelliSense trong Visual Studio. Nhập |>và bạn nhận được danh sách các hàm có thể được áp dụng cho giá trị ở bên trái, tương tự như những gì xảy ra khi nhập .sau một đối tượng.
Kristopher Johnson

32

Tôi nghĩ chúng ta đang làm mọi thứ khó hiểu. Haskell's ( .) tương đương với F # 's ( >>). Đừng nhầm với F # 's ( |>) chỉ là ứng dụng hàm đảo ngược và giống như Haskell's ( $) - đã đảo ngược:

let (>>) f g x = g (f x)
let (|>) x f = f x

Tôi tin rằng các lập trình viên Haskell $thường xuyên sử dụng . Có lẽ không thường xuyên như các lập trình viên F # có xu hướng sử dụng |>. Mặt khác, một số kẻ F # sử dụng >>ở mức độ vô lý: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx


2
như bạn nói nó giống như Haskell của $nhà điều hành - đảo ngược, bạn cũng có thể dễ dàng định nghĩa nó như là: a |> b = flip ($)mà trở nên tương đương với F # 's đường ống dẫn ví dụ bạn có thể làm[1..10] |> map f
vis

8
Tôi nghĩ ( .) giống với ( <<), trong khi ( >>) là thành phần ngược. Đó là ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer

-1 cho "sử dụng >> ở một mức độ vô lý". (Chà, tôi không làm nhưng bạn hiểu ý tôi). F trong F # là cho "chức năng", vì vậy thành phần chức năng là hợp pháp.
Florian F

1
Chắc chắn tôi đồng ý rằng thành phần chức năng là hợp pháp. Bởi "một số F # guys" Tôi đang đề cập đến bản thân tôi! Đó là blog của riêng tôi. : P
AshleyF

Tôi không nghĩ .là tương đương với >>. Tôi không biết liệu F # có <<nhưng điều đó sẽ tương đương (như trong Elm).
Matt Joiner

28

Nếu bạn muốn sử dụng F # |>trong Haskell thì trong Data.Function&toán tử (kể từ base 4.8.0.0).


Có lý do gì để Haskell chọn &qua |>không? Tôi cảm thấy |>nó trực quan hơn nhiều và nó cũng nhắc tôi về toán tử đường ống Unix.
yeshengm

16

Bố cục từ trái sang phải trong Haskell

Một số người cũng sử dụng kiểu từ trái sang phải (truyền tin nhắn) trong Haskell. Ví dụ: xem thư viện mps trên Hackage. Một ví dụ:

euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum

Tôi nghĩ rằng phong cách này trông đẹp trong một số tình huống, nhưng nó khó đọc hơn (người ta cần biết thư viện và tất cả các toán tử của nó, việc xác định lại (.)cũng gây phiền nhiễu).

Ngoài ra còn có các toán tử thành phần từ trái sang phải cũng như từ phải sang trái trong Control.Category , một phần của gói cơ sở. So sánh >>><<<tương ứng:

ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]

Có một lý do chính đáng để thỉnh thoảng thích bố cục từ trái sang phải: thứ tự đánh giá tuân theo thứ tự đọc.


Tốt, vì vậy (>>>) phần lớn tương đương với (|>)?
Khanzor

@Khanzor Không chính xác. (|>) áp dụng một đối số, (>>>) chủ yếu là thành phần hàm (hoặc những thứ tương tự). Sau đó, tôi cho rằng có một số khác biệt về độ cố định (đã không kiểm tra nó).
sastanin 17/02/12

15

Tôi đã thấy >>>nó được sử dụng cho flip (.)và bản thân tôi thường sử dụng nó, đặc biệt là cho các chuỗi dài được hiểu rõ nhất từ ​​trái sang phải.

>>> thực sự là từ Control.Arrow, và hoạt động trên nhiều chức năng.


>>>được xác định trong Control.Category.
Steven Shaw

13

Ngoài phong cách và văn hóa, điều này còn tập trung vào việc tối ưu hóa thiết kế ngôn ngữ cho mã thuần túy hoặc không tinh khiết.

Các |>nhà điều hành là phổ biến trong F # chủ yếu là do nó giúp che giấu hai hạn chế xuất hiện với mã chủ yếu-ô uế:

  • Suy luận kiểu trái sang phải không có kiểu cấu trúc con.
  • Giới hạn giá trị.

Lưu ý rằng hạn chế trước đây không tồn tại trong OCaml vì kiểu phụ là cấu trúc thay vì danh nghĩa, do đó kiểu cấu trúc dễ dàng được tinh chỉnh thông qua hợp nhất khi tiến trình suy luận kiểu.

Haskell thực hiện một sự đánh đổi khác, chọn tập trung vào mã chủ yếu thuần túy, nơi những hạn chế này có thể được loại bỏ.


8

Tôi nghĩ toán tử chuyển tiếp ống của F # ( |>) nên so với ( & ) trong haskell.

// pipe operator example in haskell

factorial :: (Eq a, Num a) =>  a -> a
factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)
// terminal
ghic >> 5 & factorial & show

Nếu bạn không thích &toán tử ( ), bạn có thể tùy chỉnh nó như F # hoặc Elixir:

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show

Tại sao infixl 1 |>? Xem tài liệu trong Data-Function (&)

infixl = infix + liên kết trái

infixr = infix + liên kết đúng


(.)

( .) có nghĩa là thành phần chức năng. Nó có nghĩa là (fg) (x) = f (g (x)) trong Toán học.

foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5

nó bằng

// (1)
foo x = negate (x * 3) 

hoặc là

// (2)
foo x = negate $ x * 3 

( $) toán tử cũng được xác định trong Data-Function ($) .

( .) được sử dụng để tạo Hight Order Functionhoặc closure in js. Xem ví dụ:


// (1) use lamda expression to create a Hight Order Function
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]


// (2) use . operator to create a Hight Order Function
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]

Chà, Ít (mã) thì tốt hơn.


So sánh |>.

ghci> 5 |> factorial |> show

// equals

ghci> (show . factorial) 5 

// equals

ghci> show . factorial $ 5 

Nó là sự khác biệt giữa left —> rightright —> left. ⊙﹏⊙ |||

Nhân hóa

|>&tốt hơn.

bởi vì

ghci> sum (replicate 5 (max 6.7 8.9))

// equals

ghci> 8.9 & max 6.7 & replicate 5 & sum

// equals

ghci> 8.9 |> max 6.7 |> replicate 5 |> sum

// equals

ghci> (sum . replicate 5 . max 6.7) 8.9

// equals

ghci> sum . replicate 5 . max 6.7 $ 8.9

Làm thế nào để lập trình chức năng trong ngôn ngữ hướng đối tượng?

vui lòng truy cập http://reactivex.io/

Hỗ trợ IT :

  • Java: RxJava
  • JavaScript: RxJS
  • C #: Rx.NET
  • C # (Unity): UniRx
  • Scala: RxScala
  • Clojure: RxClojure
  • C ++: RxCpp
  • Lua: RxLua
  • Ruby: Rx.rb
  • Python: RxPY
  • Đi: RxGo
  • Groovy: RxGroovy
  • JRuby: RxJRuby
  • Kotlin: RxKotlin
  • Swift: RxSwift
  • PHP: RxPHP
  • Elixir: reaxive
  • Dart: RxDart

1

Đây là ngày đầu tiên tôi dùng thử Haskell (sau Rust và F #), và tôi đã có thể định nghĩa toán tử |> của F #:

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>

và nó có vẻ hoạt động:

factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)

main =     
    5 |> factorial |> print

Tôi cá rằng một chuyên gia Haskell có thể cung cấp cho bạn một giải pháp thậm chí còn tốt hơn.


1
bạn cũng có thể xác định ghi vào khai thác Infix :) x |> f = f x
Jamie
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.