ฟังก์ชั่นไม่บริสุทธิ์ทันทีหากใช้ฟังก์ชั่นเป็นพารามิเตอร์หรือไม่?


17

เนื่องจากความบริสุทธิ์ของพารามิเตอร์อินพุตนั้นไม่เป็นที่รู้จักจนกระทั่งรันไทม์เป็นฟังก์ชันที่ถือว่าไม่บริสุทธิ์ทันทีหากใช้ฟังก์ชันเป็นพารามิเตอร์อินพุตหรือไม่

ที่เกี่ยวข้อง: ถ้าฟังก์ชั่นใช้ฟังก์ชั่นบริสุทธิ์ที่กำหนดไว้ด้านนอกของฟังก์ชั่น แต่ไม่ได้ผ่านเป็นพารามิเตอร์มันยังคงบริสุทธิ์ถ้ามันเป็นไปตามเกณฑ์ของการไม่มีผลข้างเคียงและเอาท์พุทขึ้นอยู่กับอินพุตเท่านั้น?

สำหรับบริบทฉันกำลังเขียนโค้ดฟังก์ชันใน JavaScript


เป็นตัวอย่างที่เคาน์เตอร์เล็กน้อยพิจารณา:foo = function(function bar){ print(bar.toString()) }
เดวิดพูดว่า Reinstate Monica

1
@DavidGrinberg นั่นไม่ใช่ตัวอย่างที่เคาน์เตอร์ฉันคิดว่าจริง ๆ แล้วเน้นปัญหาที่ใหญ่กว่า; หากคุณมีฟังก์ชั่นที่สามารถเขียนทับได้และไม่สามารถรับประกันได้ว่าการใช้งานไม่มีผลข้างเคียงคุณไม่สามารถรับประกันได้ว่าฟังก์ชั่นส่วนใหญ่ที่ใช้วัตถุและเรียกวิธีการของพวกเขานั้นบริสุทธิ์ บางทีแถบ toString () อาจลบไฟล์บางส่วนจากดิสก์หรือไม่
Joshua Taylor

3
@DavidGrinberg แต่ฉันคิดว่าคุณกำลังคิดไปในทิศทางที่ดี foo = function(function bar) { return 3; } เป็นที่บริสุทธิ์และใช้ฟังก์ชั่นเป็นอาร์กิวเมนต์
Joshua Taylor

@JoshuaTaylor Fair point ฉันไม่คิดอย่างนั้น แต่คุณได้แก้ไขปัญหาแล้ว ในฐานะที่เป็นทางเลือกอื่นให้เรียก 'รูท' toString()(เช่นที่คุณจะพบใน Object ของ Java)
เดวิดพูดว่า Reinstate Monica

คำตอบ:


22

ตราบใดที่ค่าทั้งหมดที่ใช้ในฟังก์ชั่นนั้นถูกกำหนดโดยพารามิเตอร์เท่านั้นมันเป็นฟังก์ชั่นที่แท้จริง

facet ที่เอาต์พุตเหมือนกันในแต่ละครั้งสำหรับอินพุตเดียวกันถูกควบคุมโดยไม่ว่าพารามิเตอร์นั้นจะบริสุทธิ์หรือไม่ หากคุณสมมติว่าพารามิเตอร์ (เช่นอาร์กิวเมนต์ของฟังก์ชั่น) เป็นจริงเช่นกัน

ในภาษาอย่าง Javascript ที่ไม่มีการบังคับใช้ความบริสุทธิ์หมายความว่าเป็นไปได้ที่จะทำให้ฟังก์ชั่น pure นั้นมีพฤติกรรมที่ไม่บริสุทธิ์

สิ่งนี้หมายความว่าสำหรับภาษาที่ไม่บังคับใช้ความบริสุทธิ์ (เกือบทั้งหมด) มันเป็นไปไม่ได้ที่จะกำหนดฟังก์ชั่นแท้ที่เรียกใช้ฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ มันยังมีประโยชน์ในการเขียนให้บริสุทธิ์ที่สุดและให้เหตุผลเกี่ยวกับฟังก์ชันบริสุทธิ์ แต่คุณต้องใช้ความระมัดระวังเพราะการสันนิษฐานว่ามันบริสุทธิ์จะเสียถ้าคุณผ่านการขัดแย้งที่ผิด

จากประสบการณ์ของฉันในทางปฏิบัตินี่ไม่ใช่เรื่องใหญ่ - ฉันคิดว่ามันยากที่จะมีฟังก์ชั่นที่ไม่บริสุทธิ์ถูกใช้เป็นอาร์กิวเมนต์ของฟังก์ชันกับฟังก์ชั่นที่บริสุทธิ์


เกี่ยวกับคำแถลงของคุณว่า "ตราบใดที่ค่าทั้งหมดที่ใช้ในฟังก์ชั่นนั้นถูกกำหนดโดยพารามิเตอร์เท่านั้นมันเป็นฟังก์ชั่นที่แท้จริง" จะเกิดอะไรขึ้นในกรณีของค่าคงที่? หากฉันมีฟังก์ชั่นareaOfCircle r => Math.Pi * r * rจะareaOfCircleไม่บริสุทธิ์เพราะไม่เพียง แต่ใช้พารามิเตอร์หรือไม่
David Arno

2
@DavidArno นั่นเป็นประเด็นที่ยุติธรรม อ้างอิงถึงความโปร่งใสการอ้างอิงถึงค่าคงที่ภายนอกจะไม่แตกต่างจากการเข้ารหัสแบบฮาร์ดโค้ดดังนั้นมันจะยังคงบริสุทธิ์
Daenyth

1
"นี่หมายความว่ามันเป็นไปได้ที่จะทำให้ฟังก์ชั่นบริสุทธิ์มีพฤติกรรมที่ไม่บริสุทธิ์" - โดยนิยามฟังก์ชั่นที่บริสุทธิ์ไม่สามารถมีพฤติกรรมที่ไม่บริสุทธิ์ คุณกำลังทำผิดพลาดจากการคิดว่าฟังก์ชั่นf(f2)ที่เรียกใช้f2นั้นไม่ได้พึ่งพาสิ่งที่f2ต้องพึ่งพา ฟังก์ชั่นที่อาจเรียกใช้ฟังก์ชั่นการส่งผ่านโดยพลการไม่บริสุทธิ์
user2357112 รองรับ Monica

2
@Denenyth: ดีกว่า แต่ก็ยังถือว่าว่าฟังก์ชั่นจะต้องเรียกใช้ฟังก์ชั่นที่ผ่านมา มันอาจจะเป็นสิ่งที่ชอบfunction compose(f, g) {return function h(x) {return f(g(x));};}ซึ่งบริสุทธิ์แม้จะทำหน้าที่เป็นอาร์กิวเมนต์
user2357112 รองรับ Monica

1
"ฉันคิดว่ามันยากที่จะมีฟังก์ชั่นที่ไม่บริสุทธิ์ถูกใช้เป็นอาร์กิวเมนต์ของฟังก์ชันกับฟังก์ชันที่บริสุทธิ์" - ไม่ใช่ภาษาที่ใช้งานได้ แต่ฟังก์ชั่นไลบรารี C ++ บางอันมีคำเตือนเฉพาะว่าข้อโต้แย้งเพรดิเคตต้องเป็น (โดยประมาณ) บริสุทธิ์ ดังนั้นในแง่หนึ่งนี่หมายความว่ามันไม่ได้เกิดขึ้นได้ยากเท่านั้นมันไม่เคยเกิดขึ้นอย่างถูกต้อง แต่ในอีกแง่หนึ่งความจริงที่พวกเขาต้องห้ามก็คือบางครั้งผู้คนต้องการทำเช่นนั้น ตัวอย่างเช่นพวกเขาต้องการที่จะผ่านfindรูทีนเพรดิเคตที่ไม่บริสุทธิ์ที่ส่งกลับ "จริง" สำหรับรายการที่ตรงกันที่สามที่พบหรือบางเรื่องไร้สาระ
Steve Jessop

19

เนื่องจากความบริสุทธิ์ของพารามิเตอร์อินพุตนั้นไม่เป็นที่รู้จักจนกระทั่งรันไทม์เป็นฟังก์ชันที่ถือว่าไม่บริสุทธิ์ทันทีหากใช้ฟังก์ชันเป็นพารามิเตอร์อินพุตหรือไม่

หมายเลขตัวอย่าง:

function pure(other_function) {
    return 1;
}

ไม่ว่าother_functionจะเป็นฟังก์ชั่นแท้ฟังก์ชั่นที่ไม่บริสุทธิ์หรือไม่ฟังก์ชั่นเลย pureฟังก์ชั่นบริสุทธิ์

ตัวอย่างอื่น ๆ :

function identity(x) {
    return x;
}

ฟังก์ชั่นนี้บริสุทธิ์แม้ว่าxจะเป็นฟังก์ชั่นที่ไม่บริสุทธิ์ก็ตาม identity(impure_function)จะกลับมาเสมอimpure_functionไม่ว่าคุณจะโทรซ้ำกี่ครั้ง มันไม่สำคัญว่าidentity(impure_function)()จะส่งคืนสิ่งเดียวกันเสมอ ค่าส่งคืนของฟังก์ชันจะไม่ส่งผลกระทบต่อความบริสุทธิ์


โดยทั่วไปหากฟังก์ชั่นอาจเรียกใช้ฟังก์ชั่นมันถูกส่งผ่านเป็นอาร์กิวเมนต์มันไม่บริสุทธิ์ ตัวอย่างเช่นฟังก์ชั่นfunction call(f) {f();}ไม่บริสุทธิ์เพราะแม้ว่ามันจะไม่ได้กล่าวถึงสถานะโกลบอลหรือไม่แน่นอนใด ๆ ก็ตามfอาจเป็นสิ่งalertที่ทำให้เกิดผลข้างเคียงที่มองเห็นได้

หากฟังก์ชั่นใช้ฟังก์ชั่นเป็นอาร์กิวเมนต์ แต่มันไม่ได้เรียกพวกเขาหรือทำให้พวกเขาถูกเรียกมันก็จะบริสุทธิ์ มันอาจยังไม่บริสุทธิ์ถ้าทำสิ่งที่ไม่บริสุทธิ์อื่น ๆ ยกตัวอย่างเช่นเป็นมลทินแม้ว่ามันจะไม่เคยเรียกร้องfunction f(ignored_function) {alert('This isn't pure.');}ignored_function


4
การตอบสนองนี้ดูเหมือนอวดรู้มากเกินไป เราสามารถอนุมานจากคำถามที่เกี่ยวข้องมากกว่าพารามิเตอร์ฟังก์ชันที่ถูกเรียก การมีอยู่ของฟังก์ชันที่สามารถ / ทำหน้าที่อื่น ๆ เป็นพารามิเตอร์โดยไม่ต้องเรียกใช้พวกเขาจะไม่ส่งผลกระทบต่อคำถามนี้
walpen

13
@Walpen: คำถามทำให้ไม่พูดถึงเรื่องการโต้แย้ง ไม่มีเหตุผลที่จะถือว่าผู้ถามถึงแม้ฟังก์ชั่นอาจใช้ฟังก์ชั่นอื่นเป็นอินพุตโดยไม่ต้องเรียกใช้มัน มันเป็นสิ่งสำคัญที่จะชี้ให้เห็นข้อสันนิษฐานที่ซ่อนอยู่เช่นนี้ไม่ใช่เพียงแค่สมมติว่าคุณตั้งใจจะเดามัน
user2357112 รองรับ Monica

12

เนื่องจากความบริสุทธิ์ของพารามิเตอร์อินพุตนั้นไม่เป็นที่รู้จักจนกระทั่งรันไทม์เป็นฟังก์ชันที่ถือว่าไม่บริสุทธิ์ทันทีหากใช้ฟังก์ชันเป็นพารามิเตอร์อินพุตหรือไม่

ในทางเทคนิคแล้วใช่ว่าจะมีบางวิธีในภาษาของคุณที่จะรับประกันว่าฟังก์ชั่นอินพุตยังบริสุทธิ์

ถ้าฟังก์ชั่นใช้ฟังก์ชั่นบริสุทธิ์ที่กำหนดไว้ด้านนอกของฟังก์ชั่น แต่ไม่ได้ผ่านในฐานะที่เป็นพารามิเตอร์มันยังคงบริสุทธิ์ถ้ามันเป็นไปตามเกณฑ์ของการไม่มีผลข้างเคียงและเอาท์พุทขึ้นอยู่กับอินพุตเท่านั้น?

ใช่. งั้นมาเน้นที่สิ่งที่สำคัญที่นี่ ฟังก์ชั่นการโทรบริสุทธิ์หรือไม่ไม่ได้อยู่ในตัวของมันเองและมีประโยชน์ ฟังก์ชั่นเพียวมีประโยชน์เพราะการสร้างเอาต์พุตเดียวกันสำหรับอินพุตใด ๆ และไม่ขึ้นอยู่กับสถานะหรือการมีผลข้างเคียงเป็นชุดคุณสมบัติที่มีประโยชน์มาก หมายความว่าเมื่อฟังก์ชั่นของคุณทำงานแล้วคุณสามารถ "จดจำ" คำตอบสำหรับอินพุตนั้นและมันจะเป็นจริงเสมอ คุณไม่จำเป็นต้องเรียกใช้ฟังก์ชันอีกครั้งเพื่อสร้างผลข้างเคียง และคุณสามารถเรียกใช้ฟังก์ชันนั้นแบบขนาน (หรือล้าสมัย) กับฟังก์ชั่นอื่น ๆ และรู้ว่าพวกเขาจะไม่มีปฏิสัมพันธ์ที่ซ่อนอยู่ซึ่งทำงานไม่ดี

คุณสมบัติที่มีประโยชน์เหล่านั้นยังคงอยู่หากฟังก์ชันใช้ฟังก์ชั่น pure read-only อื่น ๆ เพื่อทำงานโดยไม่คำนึงถึงวิธีการอ้างอิง


5

ตามที่ Telastyn กล่าวไว้ว่าในทางเทคนิคแล้วใช่เว้นแต่จะมีวิธีในภาษาของคุณที่จะรับประกันว่าฟังก์ชั่นการป้อนข้อมูลนั้นยังบริสุทธิ์

นั่นไม่ใช่ข้อสมมุติมีวิธีที่ดีที่จะรับประกันสิ่งนี้ อย่างน้อยก็ในภาษาที่พิมพ์ออกมาอย่างรุนแรง

ฟังก์ชั่น pure ~ ที่คุณเขียนด้วยจาวาสคริปต์ในฐานะ

function foo(f) {
   return f(1) + 2;
}

สามารถแปลโดยตรงสู่ Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

ตอนนี้ใน JavaScript คุณสามารถทำสิ่งชั่วร้ายเช่น

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

นี้เป็นไปไม่ได้ใน Haskell เหตุผลอะไรบางอย่างที่มีผลข้างเคียง - ful เช่นconsole.log()จะต้องมีประเภทของผลเสมอIO somethingไม่เพียง แต่somethingคนเดียว

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

สำหรับนิพจน์นี้เพื่อพิมพ์เช็คเราจะต้องให้fooลายเซ็นประเภท

foo :: (Int -> IO Int) -> Int

แต่มันกลับกลายเป็นว่าฉันไม่สามารถใช้มันได้อีกต่อไปแล้ว: เนื่องจากฟังก์ชั่นการโต้แย้งมีIOผลทำให้ฉันไม่สามารถใช้มันfooได้

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

วิธีเดียวที่ฉันสามารถใช้การIOกระทำfooคือถ้าผลลัพธ์ของตัวเองfooพิมพ์IO Int:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

แต่ ณ จุดนี้มันชัดเจนจากลายเซ็นของfooว่ามันไม่ได้เป็นฟังก์ชั่นที่บริสุทธิ์เช่นกัน


1
ก่อนที่คุณจะพูดว่า "เป็นไปไม่ได้" ให้ดูที่unsafeIO:-)
Bergi

2
@Bergi: จริง ๆ แล้วไม่ได้เป็นส่วนหนึ่งของ Haskell แต่มีฟังก์ชั่นอินเทอร์เฟซต่างประเทศ: เพื่อให้สามารถยืนยันได้ว่าฟังก์ชั่นที่กำหนดในภาษาอื่นนั้นบริสุทธิ์ซึ่ง Haskell คอมไพเลอร์ IOสิ่งดังกล่าวเป็น บังเอิญมันยังสามารถนำมาใช้เพื่อก่อให้เกิดการทำร้ายร่างกายโดยการซ่อนผลข้างเคียงในฟังก์ชั่น“บริสุทธิ์” แต่ที่จริงไม่ปลอดภัยใน Haskell เพราะมีไม่ได้จริงๆวิธีที่เชื่อถือได้ระบุลำดับการประเมินผลของฟังก์ชั่นที่บริสุทธิ์
leftaroundabout

ใช่นั่นเป็นความจริง. อย่างไรก็ตามฉันคิดว่าฉันเคยเห็นว่ามันเคยถูกใช้เพื่อ "ซ่อน" ผลข้างเคียงที่เป็นประโยชน์ในฟังก์ชั่น "บริสุทธิ์" เช่นกันซึ่งต้องมีการสร้างความปลอดภัยของวิธีการก่อน
Bergi

@Bergi คุณส่วนใหญ่ไม่ควรจะใช้unsafeIO; นั่นเป็นช่องทางหลบหนีสุดท้ายที่ก้าวเท้าเลี่ยงการรับรองระบบและดังนั้นคุณจึงไม่ใช่จุดที่ดี
Andres F.

0

ไม่มันไม่ใช่.

หากฟังก์ชั่นที่ผ่านมาไม่บริสุทธิ์และฟังก์ชั่นของคุณเรียกใช้ฟังก์ชั่นที่ผ่านแล้วฟังก์ชั่นของคุณจะถือว่าไม่บริสุทธิ์

ความสัมพันธ์ที่บริสุทธิ์ / ไม่บริสุทธิ์เป็นเหมือนซิงค์ / async ใน JS คุณสามารถใช้รหัสที่บริสุทธิ์ได้อย่างอิสระจากการไม่บริสุทธิ์ แต่ไม่ใช่วิธีอื่น ๆ


คำตอบนี้ไม่ได้เพิ่มสิ่งใด ๆ ที่ไม่ได้อธิบายไว้ในนี้ ... โปรดอ่านคำตอบก่อนที่จะแก้ไขสิ่ง :)
Andres F.

การซิงค์ / async คล้ายคลึงกันอย่างไร
Bobby Marinoff
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.