องค์ประกอบของ Haskell (.) กับตัวดำเนินการไปข้างหน้าของ F # (|>)


100

ใน F # การใช้ตัวดำเนินการเดินหน้าท่อ|>เป็นเรื่องปกติ อย่างไรก็ตามใน Haskell ฉันเคยเห็นแค่การใช้องค์ประกอบของฟังก์ชัน(.)เท่านั้น ฉันเข้าใจว่ามันเกี่ยวข้องกันแต่มีเหตุผลด้านภาษาหรือไม่ที่ไม่ได้ใช้ไพพ์ฟอร์เวิร์ดใน Haskell หรือเป็นอย่างอื่น


2
lihanseys คำตอบที่ระบุว่า&เป็นของ |>Haskell ฝังลึกลงไปในเธรดนี้และใช้เวลาสองสามวันในการค้นพบ ฉันใช้มันบ่อยมากเพราะคุณมักจะอ่านจากซ้ายไปขวาเพื่อติดตามรหัสของคุณ
itmuckel

คำตอบ:


61

ฉันกำลังเก็งกำไรเล็กน้อย ...

วัฒนธรรม : ฉันคิดว่า|>เป็นตัวดำเนินการที่สำคัญใน F # "วัฒนธรรม" และอาจจะคล้ายกันกับ.Haskell F # มีตัวดำเนินการจัดองค์ประกอบฟังก์ชัน<<แต่ฉันคิดว่าชุมชน F # มีแนวโน้มที่จะใช้รูปแบบที่ไม่มีจุดน้อยกว่าชุมชน Haskell

ความแตกต่างของภาษา : ฉันไม่รู้เกี่ยวกับภาษาทั้งสองมากพอที่จะเปรียบเทียบได้ แต่บางทีกฎสำหรับการสรุปการเชื่อมโยงแบบทั่วไปอาจแตกต่างกันพอสมควรที่จะส่งผลต่อสิ่งนี้ ตัวอย่างเช่นฉันรู้ว่าใน F # บางครั้งก็เขียน

let f = exp

จะไม่คอมไพล์และคุณต้องการ eta-conversion อย่างชัดเจน:

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

เพื่อให้คอมไพล์ นอกจากนี้ยังนำผู้คนออกจากรูปแบบที่ไม่มีจุด / องค์ประกอบและไปสู่รูปแบบท่อ นอกจากนี้การอนุมานประเภท F # บางครั้งต้องการการวางท่อเพื่อให้ประเภทที่รู้จักปรากฏทางด้านซ้าย (ดูที่นี่ )

(โดยส่วนตัวแล้วฉันพบว่ารูปแบบที่ไม่มีจุดอ่านไม่ได้ แต่ฉันคิดว่าทุกสิ่งใหม่ / แตกต่างดูเหมือนจะไม่สามารถอ่านได้จนกว่าคุณจะคุ้นเคยกับมัน)

ฉันคิดว่าทั้งสองอย่างสามารถทำงานได้ในภาษาใดภาษาหนึ่งและประวัติศาสตร์ / วัฒนธรรม / อุบัติเหตุอาจเป็นตัวกำหนดว่าทำไมแต่ละชุมชนจึงตั้งถิ่นฐานด้วย "ตัวดึงดูด" ที่แตกต่างกัน


7
ฉันเห็นด้วยกับความแตกต่างทางวัฒนธรรม Haskell แบบดั้งเดิมใช้ประโยชน์.และ$ผู้คนยังคงใช้มันต่อไป
Amok

9
บางครั้งชี้ให้เห็นได้ฟรีมากกว่าชี้บางครั้งน้อยกว่า โดยทั่วไปฉันจะใช้มันในการโต้แย้งกับฟังก์ชันต่างๆเช่นแผนที่และตัวกรองเพื่อหลีกเลี่ยงไม่ให้แลมบ์ดามายุ่งเหยิง บางครั้งฉันก็ใช้มันในฟังก์ชันระดับบนสุดเช่นกัน แต่มักจะน้อยลงและก็ต่อเมื่อมันตรงไปตรงมาเท่านั้น
Paul Johnson

2
ฉันไม่เห็นวัฒนธรรมมากนักในแง่ที่ว่าไม่มีทางเลือกในเรื่องนี้มากเท่าที่ F # เกี่ยวข้อง (ด้วยเหตุผลที่คุณและพระพิฆเนศกล่าวถึง) ดังนั้นฉันจะบอกว่าทั้งคู่ทำงานได้ใน Haskell แต่ F # นั้นดีกว่ามากสำหรับการใช้ตัวดำเนินการไปป์ไลน์
Kurt Schelfth ประมาณ

2
ใช่วัฒนธรรมการอ่านจากซ้ายไปขวา :) แม้แต่คณิตศาสตร์ก็ควรสอนแบบนั้น ...
ลัส

1
@nicolas ซ้ายไปขวาเป็นตัวเลือกโดยพลการ คุณสามารถใช้จากขวาไปซ้าย
Martin Capodici

86

ใน F # (|>)มีความสำคัญเนื่องจากการตรวจสอบการพิมพ์จากซ้ายไปขวา ตัวอย่างเช่น:

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

โดยทั่วไปจะไม่พิมพ์ดีดเพราะแม้ว่าxsจะทราบประเภทของอาร์กิวเมนต์xแต่ก็ไม่ทราบประเภทของอาร์กิวเมนต์ของแลมบ์ดาในขณะที่ตัวตรวจสอบตัวพิมพ์เห็นดังนั้นจึงไม่ทราบวิธีแก้ไขx.Valueเห็นมันจึงไม่ทราบวิธีการแก้ปัญหา

ในทางตรงกันข้าม

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

จะทำงานได้ดีเนื่องจากประเภทของxsจะนำไปสู่ประเภทของxการเป็นที่รู้จัก

typechecking x.Valueซ้ายไปขวาถูกต้องเนื่องจากความละเอียดชื่อที่เกี่ยวข้องในการสร้างเช่น Simon Peyton Jones ได้เขียนข้อเสนอสำหรับการเพิ่มการแก้ไขชื่อที่คล้ายกันให้กับ Haskell แต่เขาแนะนำให้ใช้ข้อ จำกัด ในท้องถิ่นเพื่อติดตามว่าประเภทรองรับการดำเนินการเฉพาะหรือไม่แทน ดังนั้นในตัวอย่างแรกข้อกำหนดที่xต้องการValueคุณสมบัติจะถูกยกยอดไปจนกว่าจะxsเห็นและข้อกำหนดนี้สามารถแก้ไขได้ สิ่งนี้จะทำให้ระบบประเภทซับซ้อนขึ้น


1
สิ่งที่น่าสนใจคือมีตัวดำเนินการ (<|) คล้ายกับ (.) ใน Haskell ที่มีทิศทางของข้อมูลจากขวาไปซ้าย แต่งานจะมีความละเอียดอย่างไร
The_Ghost

7
(<|) คล้ายกับ ($) ของ Haskell การพิมพ์จากซ้ายไปขวาจำเป็นสำหรับการแก้ไขสิ่งต่างๆเช่น. Value เท่านั้นดังนั้น (<|) จึงทำงานได้ดีในสถานการณ์อื่น ๆ หรือหากคุณใช้คำอธิบายประกอบประเภทที่ชัดเจน
GS - ขอโทษโมนิกา

44

การเก็งกำไรเพิ่มเติมคราวนี้จากด้าน Haskell ที่โดดเด่น ...

($)เป็นการพลิกกลับ(|>)และการใช้งานค่อนข้างบ่อยเมื่อคุณไม่สามารถเขียนโค้ดแบบไม่มีจุดได้ ดังนั้นเหตุผลหลักที่(|>)ไม่ได้ใช้ใน Haskell คือสถานที่นั้นถูกยึดครองไปแล้ว($)คือว่าสถานที่ที่ถูกใช้ไปแล้วโดย

นอกจากนี้จากประสบการณ์ F # เล็กน้อยฉันคิดว่า(|>)มันเป็นที่นิยมมากในรหัส F # เพราะคล้ายกับSubject.Verb(Object)โครงสร้างของ OO เนื่องจาก F # มีเป้าหมายเพื่อการรวมฟังก์ชัน / OO ที่Subject |> Verb Objectราบรื่นจึงเป็นการเปลี่ยนแปลงที่ราบรื่นสำหรับโปรแกรมเมอร์ที่ใช้งานได้ใหม่

โดยส่วนตัวแล้วฉันชอบคิดแบบซ้ายไปขวาด้วยดังนั้นฉันจึงใช้(|>)ใน Haskell แต่ฉันไม่คิดว่าคนอื่นจะทำ


สวัสดี Nathan "flip ($)" ถูกกำหนดไว้ล่วงหน้าที่ใดก็ได้ในแพลตฟอร์ม Haskell หรือไม่ ชื่อ "(|>)" ถูกกำหนดไว้แล้วใน Data.Sequence ที่มีความหมายอื่น ถ้าไม่ได้กำหนดไว้แล้วเรียกว่าอะไร? ฉันคิดว่าจะไปกับ "($>) = flip ($)"
mattbh

2
@mattbh: ไม่ใช่ว่าฉันจะพบกับ Hoogle ฉันไม่รู้เรื่องData.Sequence.|>แต่$>ดูมีเหตุผลเพื่อหลีกเลี่ยงความขัดแย้งที่นั่น จริงๆแล้วมีเพียงโอเปอเรเตอร์หน้าตาดีจำนวนมากดังนั้นฉันจะใช้|>สำหรับทั้งสองอย่างและจัดการความขัดแย้งเป็นกรณี ๆ ไป (นอกจากนี้ฉันจะถูกล่อลวงให้ใช้นามแฝงData.Sequence.|>ว่าsnoc)
Nathan Shively-Sanders

2
($)และ(|>)เป็นแอปพลิเคชันที่ไม่มีองค์ประกอบ ทั้งสองมีความสัมพันธ์กัน (ตามที่บันทึกคำถาม) แต่ไม่เหมือนกัน (ของคุณfcมี(Control.Arrow.>>>)ไว้สำหรับฟังก์ชัน)
Nathan Shively-Sanders

11
ในทางปฏิบัติ F # |>ทำให้ฉันนึกถึง UNIX |มากกว่าสิ่งอื่นใด
Kevin Cantu

3
ข้อดีอีกอย่างของ|>F # คือมีคุณสมบัติที่ดีสำหรับ IntelliSense ใน Visual Studio พิมพ์|>และคุณจะได้รับรายการฟังก์ชันที่สามารถนำไปใช้กับค่าทางด้านซ้ายคล้ายกับสิ่งที่เกิดขึ้นเมื่อพิมพ์.หลังวัตถุ
Kristopher Johnson

32

ฉันคิดว่าเรากำลังสับสน Haskell's ( .) เทียบเท่ากับ F # ( >>) อย่าสับสนกับ F # 's ( |>) ซึ่งเป็นเพียงแอปพลิเคชันฟังก์ชันกลับด้านและเหมือนกับ Haskell's ( $) - ย้อนกลับ:

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

ฉันเชื่อว่าโปรแกรมเมอร์ของ Haskell $มักจะใช้ อาจจะไม่บ่อยเท่าที่ F # |>โปรแกรมเมอร์มีแนวโน้มที่จะใช้งาน ในทางกลับกัน F # บางคนใช้>>ในระดับที่ไร้สาระ: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx


2
ตามที่คุณบอกว่ามันเป็นเช่น Haskell ของ$ผู้ประกอบการ - กลับคุณยังสามารถกำหนดได้อย่างง่ายดายเช่นa |> b = flip ($)ซึ่งจะกลายเป็นเทียบเท่ากับ F # 's ท่อเช่นนั้นคุณสามารถทำได้[1..10] |> map f
Vis

8
ฉันคิดว่า ( .) เหมือนกับ ( <<) ในขณะที่ ( >>) คือองค์ประกอบย้อนกลับ นั่นคือ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer

-1 สำหรับ "ใช้ >> ในระดับที่ไร้สาระ" (ฉันไม่รู้ แต่คุณเข้าใจแล้ว) F ใน F # มีไว้สำหรับ "ใช้งานได้" ดังนั้นองค์ประกอบของฟังก์ชันจึงถูกต้อง
Florian F

1
แน่นอนฉันยอมรับว่าองค์ประกอบของฟังก์ชันนั้นถูกต้อง โดย "พวก F # บางคน" ฉันหมายถึงตัวเอง! ที่บล็อกของฉันเอง : P
AshleyF

ฉันไม่คิดว่า.จะเทียบเท่ากับ>>. ฉันไม่รู้ว่ามี F # หรือเปล่า<<แต่มันจะเทียบเท่า (เหมือนใน Elm)
Matt Joiner

28

หากคุณต้องการใช้ F # |>ใน Haskell ดังนั้นในData Functionคือตัว&ดำเนินการ (ตั้งแต่base 4.8.0.0)


เหตุผลใดที่ทำให้ Haskell เลือก&มากกว่านี้|>? ฉันรู้สึกว่า|>ใช้งานง่ายกว่ามากและยังทำให้ฉันนึกถึงตัวดำเนินการท่อ Unix ด้วย
yeshengm

@yeshengm ฉันพบว่า&ใช้งานง่ายมาก โค้ดเกือบจะอ่านได้อย่างถูกต้องในภาษาอังกฤษเพียงแค่ออกเสียง&เป็น "และ" ตัวอย่างเช่น: 5 & factorial & showอ่านออกเสียงว่า "take 5 แล้วหาแฟกทอเรียลจากนั้นจึงใช้ show to it"
Marcel Besixdouze

16

องค์ประกอบจากซ้ายไปขวาใน Haskell

บางคนใช้รูปแบบซ้ายไปขวา (ส่งข้อความ) ใน Haskell ด้วย ดูตัวอย่างเช่นไลบรารีmpsใน Hackage ตัวอย่าง:

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

ฉันคิดว่าสไตล์นี้ดูดีในบางสถานการณ์ แต่อ่านยากกว่า (ต้องรู้จักไลบรารีและตัวดำเนินการทั้งหมดคำจำกัดความใหม่(.)ก็รบกวนเช่นกัน)

นอกจากนี้ยังมีตัวดำเนินการจัดองค์ประกอบแบบซ้ายไปขวาและขวาไปซ้ายในControl.Categoryซึ่งเป็นส่วนหนึ่งของแพ็กเกจพื้นฐาน เปรียบเทียบ>>>และ<<<ตามลำดับ:

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

มีเหตุผลที่ดีที่จะชอบการจัดองค์ประกอบแบบซ้ายไปขวาในบางครั้ง: ลำดับการประเมินตามลำดับการอ่าน


ดีดังนั้น (>>>) ส่วนใหญ่เท่ากับ (|>)?
Khanzor

@Khanzor ไม่เป๊ะ. (|>) ใช้อาร์กิวเมนต์ (>>>) ส่วนใหญ่เป็นองค์ประกอบของฟังก์ชัน (หรือสิ่งที่คล้ายกัน) จากนั้นฉันคิดว่ามีความแตกต่างบางอย่าง (ไม่ได้ตรวจสอบ)
sastanin

15

ฉันเคยเห็นว่ามัน>>>ถูกใช้flip (.)และฉันมักจะใช้มันเองโดยเฉพาะอย่างยิ่งสำหรับโซ่ยาวที่เข้าใจได้ดีที่สุดจากซ้ายไปขวา

>>> จริงๆแล้วมาจาก Control.Arrow และทำงานได้มากกว่าฟังก์ชัน


>>>ถูกกำหนดไว้ในControl.Category.
Steven Shaw

13

นอกเหนือจากรูปแบบและวัฒนธรรมแล้วสิ่งนี้ยังช่วยเพิ่มประสิทธิภาพการออกแบบภาษาสำหรับรหัสบริสุทธิ์หรือไม่บริสุทธิ์

ตัว|>ดำเนินการเป็นเรื่องปกติใน F # ส่วนใหญ่เนื่องจากช่วยซ่อนข้อ จำกัด สองประการที่ปรากฏพร้อมกับรหัสที่ไม่บริสุทธิ์เป็นส่วนใหญ่:

  • การอนุมานประเภทซ้ายไปขวาโดยไม่มีโครงสร้างย่อย
  • ข้อ จำกัด ของค่า

โปรดทราบว่าข้อ จำกัด เดิมไม่มีอยู่ใน OCaml เนื่องจากการพิมพ์ย่อยเป็นโครงสร้างแทนที่จะเป็นเพียงเล็กน้อยดังนั้นประเภทโครงสร้างจึงสามารถปรับแต่งได้อย่างง่ายดายผ่านการรวมกันเมื่อการอนุมานประเภทดำเนินไป

Haskell ทำการแลกเปลี่ยนที่แตกต่างกันโดยเลือกที่จะมุ่งเน้นไปที่โค้ดส่วนใหญ่ที่บริสุทธิ์ซึ่งสามารถยกเลิกข้อ จำกัด เหล่านี้ได้


8

ฉันคิดว่าตัวดำเนินการไปป์ฟอร์เวิร์ดของ F # |>ควรเทียบกับ( & )ใน 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

หากคุณไม่ชอบ&ตัวดำเนินการ( ) คุณสามารถกำหนดเองได้เช่น F # หรือ Elixir:

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

ทำไมinfixl 1 |>? ดูเอกสารในฟังก์ชันข้อมูล (&)

infixl = infix + การเชื่อมโยงด้านซ้าย

infixr = infix + right Associativity


(.)

( .) หมายถึงองค์ประกอบของฟังก์ชัน หมายถึง(fg) (x) = f (g (x))ในคณิตศาสตร์

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

มันเท่ากับ

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

หรือ

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

( $) ผู้ประกอบการยังเป็นให้คำจำกัดความในข้อมูลฟังก์ชั่น ($)

( .) ใช้สำหรับสร้างหรือHight Order Function closure in jsดูตัวอย่าง:


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

ว้าวน้อยกว่า (รหัส) ดีกว่า


เปรียบเทียบ|>และ.

ghci> 5 |> factorial |> show

// equals

ghci> (show . factorial) 5 

// equals

ghci> show . factorial $ 5 

มันแตกต่างกันระหว่างและleft —> right right —> left⊙﹏⊙ |||

ความเป็นมนุษย์

|> และ &ดีกว่า.

เพราะ

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

วิธีการเขียนโปรแกรมเชิงฟังก์ชันในภาษาเชิงวัตถุ?

โปรดไปที่http://reactivex.io/

รองรับ:

  • Java: RxJava
  • JavaScript: RxJS
  • C #: Rx.NET
  • C # (Unity): UniRx
  • สกาล่า: RxScala
  • Clojure: RxClojure
  • C ++: RxCpp
  • ลัวะ: RxLua
  • ทับทิม: Rx.rb
  • Python: RxPY
  • ไป: RxGo
  • Groovy: RxGroovy
  • JRuby: RxJRuby
  • Kotlin: RxKotlin
  • Swift: RxSwift
  • PHP: RxPHP
  • Elixir: reaxive
  • โผ: RxDart

1

นี่เป็นวันแรกของฉันในการลองใช้ Haskell (หลังจาก Rust และ F #) และฉันสามารถกำหนดตัวดำเนินการ F # s |>:

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

และดูเหมือนว่าจะใช้งานได้:

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

main =     
    5 |> factorial |> print

ฉันพนันได้เลยว่าผู้เชี่ยวชาญของ Haskell สามารถให้ทางออกที่ดียิ่งขึ้นแก่คุณได้


1
คุณยังสามารถกำหนดตัวดำเนินการ infix infix :) x |> f = f x
Jamie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.