Komposisi Haskell (.) Vs operator penerusan pipa F # (|>)

100

Di F #, penggunaan operator pipa-maju |>,, cukup umum. Namun, di Haskell saya hanya pernah melihat komposisi fungsi (.),, sedang digunakan. Saya memahami bahwa keduanya terkait , tetapi adakah alasan bahasa yang membuat pipa maju tidak digunakan di Haskell atau ada hal lain?

Ben Lings
sumber
2
Jawaban lihanseys menyatakan bahwa itu &adalah milik Haskell |>. Terkubur jauh di dalam utas ini dan butuh beberapa hari untuk saya temukan. Saya sering menggunakannya, karena Anda biasanya membaca dari kiri ke kanan untuk mengikuti kode Anda.
itmuckel

Jawaban:

61

Saya menjadi sedikit spekulatif ...

Budaya : Saya pikir |>adalah operator penting dalam "budaya" F #, dan mungkin serupa dengan .Haskell. F # memiliki operator komposisi fungsi <<tetapi menurut saya komunitas F # cenderung menggunakan gaya bebas poin lebih sedikit daripada komunitas Haskell.

Perbedaan bahasa : Saya tidak cukup tahu tentang kedua bahasa untuk dibandingkan, tetapi mungkin aturan untuk menggeneralisasi let-binding cukup berbeda untuk memengaruhi hal ini. Misalnya, saya tahu di F # kadang-kadang menulis

let f = exp

tidak akan dikompilasi, dan Anda memerlukan konversi eta eksplisit:

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

untuk membuatnya dikompilasi. Ini juga menjauhkan orang dari gaya bebas poin / komposisi, dan menuju gaya pipelining. Juga, inferensi tipe F # terkadang menuntut pipelining, sehingga tipe yang diketahui muncul di sebelah kiri (lihat di sini ).

(Secara pribadi, saya menemukan gaya bebas poin tidak dapat dibaca, tetapi saya kira setiap hal baru / berbeda tampaknya tidak dapat dibaca sampai Anda terbiasa.)

Saya pikir keduanya berpotensi layak dalam kedua bahasa, dan sejarah / budaya / kecelakaan dapat menentukan mengapa setiap komunitas menetap di "penarik" yang berbeda.

Brian
sumber
7
Saya setuju dengan perbedaan budaya. Haskell tradisional menggunakan .dan $, jadi orang terus menggunakannya.
Amuk
9
Point free terkadang lebih mudah dibaca daripada pointful, terkadang kurang. Saya biasanya menggunakannya dalam argumen untuk fungsi seperti map dan filter, untuk menghindari lambda mengacaukan semuanya. Saya terkadang menggunakannya di fungsi tingkat atas juga, tetapi lebih jarang dan hanya jika itu sesuatu yang mudah.
Paul Johnson
2
Saya tidak melihat banyak budaya di dalamnya, dalam arti bahwa tidak banyak pilihan dalam masalah ini sejauh menyangkut F # (karena alasan Anda dan Ganesh menyebutkan). Jadi saya akan mengatakan bahwa keduanya layak di Haskell, tetapi F # jelas jauh lebih siap untuk menggunakan operator pipeline.
Kurt Schelfthout
2
ya, budaya membaca kiri ke kanan :) bahkan matematika harus diajarkan seperti itu ...
nicolas
1
@nicolas dari kiri ke kanan adalah pilihan yang sewenang-wenang. Anda bisa membiasakan diri dari kanan ke kiri.
Martin Capodici
86

Di F # (|>)penting karena pemeriksaan ketik kiri-ke-kanan. Sebagai contoh:

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

umumnya tidak akan memeriksa kesalahan ketik, karena meskipun jenisnya xsdiketahui, jenis argumen xke lambda tidak diketahui pada saat pemeriksa ketik melihatnya, jadi tidak tahu bagaimana menyelesaikannya x.Value.

Sebaliknya

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

akan berfungsi dengan baik, karena jenis xsakan mengarah pada jenis xyang diketahui.

Pemeriksaan ketik kiri-ke-kanan diperlukan karena resolusi nama yang terlibat dalam konstruksi seperti x.Value. Simon Peyton Jones telah menulis proposal untuk menambahkan jenis resolusi nama yang serupa ke Haskell, tetapi ia menyarankan menggunakan batasan lokal untuk melacak apakah suatu jenis mendukung operasi tertentu atau tidak. Jadi dalam sampel pertama persyaratan yang xmembutuhkan Valueproperti akan diteruskan sampai xsterlihat dan persyaratan ini dapat diselesaikan. Ini memang memperumit sistem tipe.

GS - Minta maaf pada Monica
sumber
1
Menariknya, ada operator (<|) yang mirip dengan (.) Di Haskell dengan arah data yang sama dari kanan ke kiri. Tapi bagaimana resolusi jenis kerja untuk itu?
The_Ghost
7
(<|) sebenarnya mirip dengan ($) Haskell. Pemeriksaan ketik kiri-ke-kanan hanya diperlukan untuk menyelesaikan hal-hal seperti .Value, jadi (<|) berfungsi dengan baik di skenario lain, atau jika Anda menggunakan anotasi tipe eksplisit.
GS - Minta maaf kepada Monica
44

Lebih banyak spekulasi, kali ini dari sisi yang didominasi Haskell ...

($)adalah kebalikan dari (|>), dan penggunaannya cukup umum ketika Anda tidak dapat menulis kode tanpa poin. Jadi alasan utama yang (|>)tidak digunakan di Haskell adalah karena tempatnya sudah diambil alih ($).

Juga, berbicara dari sedikit pengalaman F #, saya pikir (|>)sangat populer di kode F # karena menyerupai Subject.Verb(Object)struktur OO. Karena F # bertujuan untuk integrasi fungsional / OO yang mulus, Subject |> Verb Objectmerupakan transisi yang cukup mulus untuk programmer fungsional baru.

Secara pribadi, saya juga suka berpikir dari kiri ke kanan, jadi saya menggunakan (|>)di Haskell, tapi menurut saya tidak banyak orang yang melakukannya.

Nathan Shively-Sanders
sumber
Hai Nathan, apakah "flip ($)" telah ditentukan sebelumnya di mana saja di platform Haskell? Nama "(|>)" sudah didefinisikan dalam Data.Sequence dengan arti lain. Jika belum ditentukan, apa sebutannya? Saya berpikir untuk pergi dengan "($>) = flip ($)"
mattbh
2
@mattbh: Bukan yang bisa saya temukan dengan Hoogle. Saya tidak tahu tentang Data.Sequence.|>, tetapi $>terlihat masuk akal untuk menghindari konflik di sana. Sejujurnya, hanya ada begitu banyak operator yang tampan, jadi saya hanya akan menggunakan |>keduanya dan mengelola konflik berdasarkan kasus per kasus. (Juga saya akan tergoda untuk hanya alias Data.Sequence.|>sebagai snoc)
Nathan Shively-Sanders
2
($)dan (|>)apakah aplikasi bukan komposisi. Keduanya berhubungan (sebagai catatan pertanyaan) tetapi mereka tidak sama (Anda fcadalah (Control.Arrow.>>>)untuk fungsi).
Nathan Shively-Sanders
11
Dalam prakteknya, F # |>sebenarnya mengingatkan saya pada UNIX |lebih dari apapun.
Kevin Cantu
3
Manfaat lain dari |>di F # adalah memiliki properti yang bagus untuk IntelliSense di Visual Studio. Ketik |>, dan Anda mendapatkan daftar fungsi yang dapat diterapkan ke nilai di sebelah kiri, mirip dengan apa yang terjadi saat mengetik .setelah objek.
Kristopher Johnson
32

Saya pikir kami sedang membingungkan. Haskell's ( .) setara dengan F #'s ( >>). Jangan bingung dengan F # 's ( |>) yang hanya aplikasi fungsi terbalik dan seperti Haskell's ( $) - terbalik:

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

Saya yakin programmer Haskell memang $sering menggunakannya . Mungkin tidak sesering pemrogram F # cenderung menggunakan |>. Di sisi lain, beberapa orang F # menggunakan >>gelar yang konyol: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx

AshleyF
sumber
2
seperti yang Anda katakan ini seperti $operator Haskell - terbalik, Anda juga dapat dengan mudah mendefinisikannya sebagai: a |> b = flip ($)yang setara dengan pipeline F #, misalnya Anda dapat melakukannya[1..10] |> map f
vis
8
Menurut saya ( .) sama dengan ( <<), sedangkan ( >>) adalah komposisi kebalikannya. Yaitu ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer
-1 untuk "menggunakan >> sampai tingkat yang konyol". (Yah, saya tidak melakukannya tetapi Anda mengerti maksud saya). F di F # adalah untuk "fungsional", jadi komposisi fungsi sah.
Florian F
1
Tentu saya setuju bahwa komposisi fungsi itu sah. Yang saya maksud dengan "beberapa orang F #" adalah diri saya sendiri! Itu blog saya sendiri. : P
AshleyF
Saya tidak berpikir .sama dengan >>. Saya tidak tahu apakah F # memiliki <<tetapi itu akan menjadi setara (seperti di Elm).
Matt Joiner
28

Jika Anda ingin menggunakan F # |>di Haskell maka di Data.Function adalah &operatornya (sejak base 4.8.0.0).

jhegedus.dll
sumber
Apa alasannya untuk Haskell untuk memilih &lebih |>? Saya merasa |>jauh lebih intuitif, dan itu juga mengingatkan saya pada operator pipa Unix.
yeshengm
16

Komposisi kiri-ke-kanan di Haskell

Beberapa orang menggunakan gaya kiri-ke-kanan (penyampaian pesan) di Haskell juga. Lihat, misalnya, pustaka mps di Hackage. Sebuah contoh:

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

Saya pikir gaya ini terlihat bagus dalam beberapa situasi, tetapi lebih sulit untuk dibaca (seseorang perlu mengetahui perpustakaan dan semua operatornya, definisi ulang (.)juga mengganggu).

Ada juga operator komposisi kiri-ke-kanan serta kanan-ke-kiri di Control.Category , bagian dari paket dasar. Bandingkan >>>dan <<<masing - masing:

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

Terkadang ada alasan bagus untuk memilih komposisi kiri-ke-kanan: urutan evaluasi mengikuti urutan pembacaan.

sastanin
sumber
Bagus, jadi (>>>) sebagian besar bisa disamakan dengan (|>)?
Khanzor
@Kor. Tidak juga. (|>) menerapkan argumen, (>>>) sebagian besar adalah komposisi fungsi (atau hal serupa). Lalu saya kira ada beberapa perbedaan ketetapan (tidak memeriksanya).
sastanin
15

Saya telah melihat >>>digunakan untuk flip (.), dan saya sering menggunakannya sendiri, terutama untuk rantai panjang yang paling baik dipahami dari kiri-ke-kanan.

>>> sebenarnya dari Control.Arrow, dan bekerja pada lebih dari sekadar fungsi.

spookylukey
sumber
>>>didefinisikan dalam Control.Category.
Steven Shaw
13

Selain gaya dan budaya, ini bermuara pada pengoptimalan desain bahasa untuk kode murni atau tidak murni.

The |>operator adalah umum di F # sebagian besar karena membantu menyembunyikan dua keterbatasan yang muncul dengan kode dominan-tidak murni:

  • Jenis inferensi kiri-ke-kanan tanpa subtipe struktural.
  • Pembatasan nilai.

Perhatikan bahwa batasan sebelumnya tidak ada di OCaml karena subtipe bersifat struktural, bukan nominal, sehingga tipe struktural mudah disempurnakan melalui penyatuan saat inferensi tipe berlangsung.

Haskell mengambil trade-off yang berbeda, memilih untuk fokus pada kode yang didominasi murni di mana batasan ini dapat dicabut.

JD
sumber
8

Saya pikir operator pipa forward F # ( |>) harus vs ( & ) di 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

Jika Anda tidak menyukai &operator ( ), Anda dapat menyesuaikannya seperti F # atau Elixir:

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

Kenapa infixl 1 |>? Lihat dokumen di Data-Function (&)

infixl = infix + asosiatif kiri

infixr = infix + asosiatif kanan


(.)

( .) berarti komposisi fungsi. Artinya (fg) (x) = f (g (x)) dalam Matematika.

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

itu sama

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

atau

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

Operator ( $) juga didefinisikan dalam Data-Function ($) .

( .) digunakan untuk membuat Hight Order Functionatau closure in js. Lihat contoh:


// (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]

Wow, Lebih sedikit (kode) lebih baik.


Bandingkan |>dan.

ghci> 5 |> factorial |> show

// equals

ghci> (show . factorial) 5 

// equals

ghci> show . factorial $ 5 

Ini adalah perbedaan antara left —> rightdan right —> left. ⊙﹏⊙ ||i>

Humanisasi

|>dan &lebih baik dari.

karena

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

Bagaimana pemrograman fungsional dalam bahasa berorientasi objek?

silakan kunjungi http://reactivex.io/

Ini mendukung:

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

Ini adalah hari pertama saya mencoba Haskell (setelah Rust dan F #), dan saya bisa mendefinisikan operator F #'s |>:

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

dan sepertinya berhasil:

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

main =     
    5 |> factorial |> print

Saya yakin pakar Haskell dapat memberi Anda solusi yang lebih baik.

tib
sumber
1
Anda juga dapat menentukan operator infix infix :) x |> f = f x
Jamie