2 คะแนน โดย GN⁺ 2024-11-18 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

บันทึกหลังเรียนคอร์ส SICP กับ David Beazley: ประสบการณ์ 1 สัปดาห์

ขอแบ่งปันประสบการณ์จากการเข้าร่วมคอร์ส SICP ของ David Beazley ช่วงปลายปี 2022 แม้จะมีสื่อฟรีอยู่มากมาย แต่คอร์สของ Dave มีประสิทธิภาพมากเพราะเลือกหัวข้อเฉพาะมาอธิบายอย่างลึกซึ้ง

จุดเริ่มต้น

คอร์ส SICP ใช้ภาษา Scheme และในที่นี้ได้อธิบายแนวคิดพื้นฐานอย่างโมเดล การแทนที่ค่า (substitution) ผ่านการสร้างตัวแปล Scheme แบบง่าย ๆ ด้วย Python

พื้นฐานของภาษา Scheme

  • Primitive: ค่าพื้นฐาน (เช่น จำนวนเต็ม)
  • Operator: ใช้การดำเนินการพื้นฐานอย่าง +, -, *, / ในรูปแบบ prefix notation
  • define: นิยามตัวแปร
> (define x 2)  
> (+ x 3) ; 결과: 5  
  • if: คำสั่งเงื่อนไข
  • lambda: นิยามฟังก์ชันนิรนาม
> ((lambda (x) (* x x)) 3) ; 결과: 9  

ตัวแปล Scheme ใน Python

มีการสร้างตัวแปลอย่างง่ายสำหรับประเมินโค้ด Scheme โดยใช้ Python และนิยามการดำเนินการพื้นฐานด้วยฟังก์ชัน Python

definitions = {  
    "+": lambda x, y: x + y,  
    "*": lambda x, y: x * y,  
}  

ตัวอย่าง:

> evaluate(("+", 2, 3)) # 결과: 5  

รวมถึงการทำงานของ define และ lambda ตลอดจนการจัดการคำสั่งเงื่อนไข if

โมเดลการแทนที่ค่า (Substitution Model)

โมเดลการแทนที่ค่าเป็นวิธีตีความโปรแกรมแบบง่าย โดยประเมินโปรแกรมผ่านการแทนตัวแปรด้วยค่า แต่เมื่อมี assignment โมเดลนี้จะใช้ไม่ได้

สถานะ (State)

ตัวอย่างที่ทำให้โมเดลการแทนที่ค่าใช้ไม่ได้คือ assignment เช่น เมื่อต้องจำลองยอดเงินคงเหลือในบัญชีธนาคาร จะใช้ set! เพื่ออัปเดตตัวแปร

(define balance 100)  
  
(define (withdraw amount)  
  (set! balance (- balance amount))  
  balance)  

ในกรณีนี้ โมเดลการแทนที่ค่าไม่สามารถแยกความต่างของสถานะยอดคงเหลือก่อนและหลังได้

จึงจำเป็นต้องใช้โมเดล Environment ตัวแปรจะถูกนิยามภายใน environment และแต่ละ procedure จะมี environment ของตัวเอง

สตรีม (Streams)

อีกวิธีหนึ่งในการจำลองสถานะคือ stream ซึ่งสามารถจำลองค่าของอนาคตได้ผ่านการประเมินค่าแบบหน่วงเวลา (lazy evaluation)

ลูปไม่สิ้นสุดและลำดับการประเมินค่า

ความต่างของลำดับการประเมินค่า: ภาษาส่วนใหญ่ใช้ applicative-order evaluation ซึ่งจะประเมินอาร์กิวเมนต์ก่อน

> (square (+ 1 2)) ; 결과: 9  

แต่ normal-order evaluation จะเลื่อนการประเมินอาร์กิวเมนต์ออกไปจนกว่าจะจำเป็นจริง ๆ ทำให้หลีกเลี่ยงลูปไม่สิ้นสุดได้

> (define (p) (p))  
> (define (test x y) (if (= x 0) 0 y))  
> (test 0 (p)) ; normal order จะคืนค่า 0 แต่ applicative order จะเข้าสู่ลูปไม่สิ้นสุด  

Lambda Calculus และเลข Church

สามารถใช้ Church encoding เพื่อแทนตัวเลขเป็น procedure ได้ ซึ่งเป็นแนวคิดสำคัญของการเขียนโปรแกรมเชิงฟังก์ชัน

(define (zero f) (lambda (x) x))  
(define (increment n) (lambda (f) (lambda (x) (f ((n f) x)))))  
  • zero คือฟังก์ชันที่คืนอาร์กิวเมนต์เดิม (identity function)
  • increment คือการเพิ่มการเรียกใช้ฟังก์ชันอีกหนึ่งครั้ง

ตัวอย่าง

> ((zero (lambda (x) (+ x 1))) 0) ; 결과: 0  
> (((increment zero) (lambda (x) (+ x 1))) 0) ; 결과: 1  

การวนซ้ำ vs การเรียกซ้ำ

Scheme ใช้ recursion แทน for loop ในการทำงานซ้ำ

ตัวอย่างการเรียกซ้ำ: แฟกทอเรียล

(define (factorial n)  
  (if (= n 1)   
    1   
    (* n (factorial (- n 1)))))  

การเรียกซ้ำลักษณะนี้อาจใช้หน่วยความจำมากเพราะต้องอาศัยสแตก

การเพิ่มประสิทธิภาพ tail-call

Scheme ลดการใช้หน่วยความจำผ่าน tail-call optimization ทำให้การทำงานมีลักษณะเหมือนกระบวนการแบบวนซ้ำ (iterative)

(define (factorial n)  
  (define (iter product counter)  
    (if (> counter n)  
        product  
        (iter (* product counter) (+ counter 1))))  
  (iter 1 1))  

สรุป

คอร์สของ David Beazley คัดเลือกแนวคิดสำคัญของ SICP มานำเสนออย่างลึกซึ้ง โดยเฉพาะเรื่องการเขียนโปรแกรมเชิงฟังก์ชัน Lambda Calculus และลำดับการประเมินค่า ซึ่งช่วยให้เข้าใจพาราไดม์การเขียนโปรแกรมที่หลากหลายมากขึ้น

คำคมของ Knuth

ถ้าคุณเรียนแต่ทฤษฎี นั่นหมายความว่าถึงเวลาที่ควรหันไปโฟกัสด้านปฏิบัติ และถ้าคุณทำแต่ภาคปฏิบัติ นั่นหมายความว่าถึงเวลาที่ควรหันไปโฟกัสด้านทฤษฎี

1 ความคิดเห็น

 
GN⁺ 2024-11-18
ความคิดเห็นจาก Hacker News
  • ขอเตือนไว้ก่อนว่า ถ้าคุณลงลึกกับ SICP/Lisp/Scheme มากพอ วิธีคิดเรื่องการเขียนโปรแกรมของคุณอาจเปลี่ยนไปได้ และแรงกระตุ้นทางปัญญาแบบนั้นก็น่ายินดีเสมอ
    แต่ถ้านำแนวคิดเหล่านั้นไปใช้ทั้งดุ้นกับโค้ดเบสเชิงวัตถุ ก็มักจะให้ผลย้อนกลับหรือทำให้เพื่อนร่วมทีมต่อต้าน
    ตัวอย่างเช่น หลังจากเจอ Lisp แล้ว คุณอาจอยากเปลี่ยน for loop ทั้งหมดเป็น forEach หรือทำทุกอย่างให้กลายเป็นสาย map/reduce แต่ถ้าภาษาไม่ได้รองรับการเขียนโปรแกรมเชิงฟังก์ชันอย่างเต็มตัว ก็อาจทำลายทั้ง ความอ่านง่ายและประสิทธิภาพ ได้
    ท้ายที่สุด การจำไว้ว่าโค้ดถูกรันโดยหน่วยความจำที่เปลี่ยนแปลงได้และ CPU จะช่วยยึดโยงพื้นฐานเอาไว้ และทุกวันนี้แนวคิดนามธรรมอย่าง Church numerals ก็ดูจะใช้งานจริงในชีวิตประจำวันได้น้อยกว่าการออกแบบที่ยึดข้อมูลเป็นศูนย์กลางและ “mechanical sympathy” ที่สอดคล้องกับความเป็นจริงของฮาร์ดแวร์

    • ผมมองว่าความไม่เปลี่ยนแปลงค่าให้ความปลอดภัยมากกว่า
      ยิ่งมีผลข้างเคียงน้อยก็ยิ่งคาดเดาได้ง่ายกว่า และการเขียนเชิงวัตถุก็มักถูกใช้แม้ในจุดที่แค่ฟังก์ชันก็เพียงพอ
      ในการพัฒนาเกม การเขียนเชิงวัตถุเหมาะพอสมควรเพราะมีทั้งการคำนวณและโค้ดที่เสี่ยงอันตรายจำนวนมาก แต่ในการพัฒนาเว็บ การเขียนเชิงฟังก์ชันเป็นธรรมชาติกว่ามาก และใน SaaS ภาษาตระกูลอย่าง Elixir ก็ช่วยให้เขียนโค้ดที่เชื่อถือได้มากกว่า บั๊กน้อยกว่า และทดสอบได้ง่ายกว่า
    • นึกว่าอย่าง Common Lisp Object System ก็มีอยู่ไม่ใช่หรือ
      อีกอย่าง forEach, map/reduce แบบนี้ก็มีอยู่แล้วในคอลเลกชันของ Smalltalk และถึงจะไม่ใช่ไวยากรณ์ชั้นหนึ่งก็ยังถูกคัดลอกไปยัง Object Pascal และ C++
      และเพราะหน่วยความจำชั้นล่างยังเปลี่ยนแปลงได้อยู่ เวลาจำเป็นจริง ๆ ภาษาตระกูล ML ก็ยังมีกลไกสำหรับจัดการ mutation เช่นกัน
    • แม้วิธีเดียวที่ CPU ใช้ทำ control flow จะใกล้เคียงกับ goto แต่ goto ก็ยิ่งไม่เป็นที่นิยมกว่า Lisp เสียอีก
      ที่แปลกยิ่งกว่าคือ แม้ว่า cache จะจำเป็นต่อประสิทธิภาพบน CPU สมัยใหม่ แต่ก็ยังไม่มีภาษาที่ปฏิบัติต่อชั้นลำดับหน่วยความจำเสมือนเป็นพลเมืองชั้นหนึ่ง
      สิ่งที่ใกล้ที่สุดดูจะเป็น C แบบสาย Linux ที่มีขีดล่างเยอะราวกับทะเล
  • เป็นบทนำที่ดีในการ เข้ารหัสสถานะ ด้วยฟังก์ชันบริสุทธิ์
    ในทางปฏิบัติยังมีการเข้ารหัสเชิงฟังก์ชันแบบบริสุทธิ์สำหรับข้อมูลแทบทุกชนิดอีกมาก ทั้ง tree, integer, sum/product type, image, monad และอื่น ๆ
    การเข้ารหัสอาจชวนสับสนอยู่บ้าง แต่ก็ทั้งสง่างามและกะทัดรัดในเวลาเดียวกัน
    ตัวอย่างเช่น ถ้าจะอิมพลีเมนต์ Maybe monad แบบเชิงฟังก์ชันใน JavaScript ก็จะได้ประมาณนี้:
    Nothing = nothing => just => nothing
    Just = v => nothing => just => just(v)
    pure = Just
    bind = mx => f => mx(mx)(f)
    evalMaybe = maybe => maybe("Nothing")(v => "Just " + v)
    console.log(evalMaybe(bind(Nothing)(n => pure(n + 1)))) // Nothing
    console.log(evalMaybe(bind(Just(42))(n => pure(n + 1)))) // Just 43

    • การอิมพลีเมนต์แบบนี้สามารถอนุมานได้จาก หลักการเรียกซ้ำ ของ type นั้น
      data Maybe a = Nothing | Just a
      foldMaybe :: (Unit -> r) -> (a -> r) -> Maybe a -> r
      ฟังก์ชันระดับสูงสองตัวที่ส่งให้ foldMaybe นั้นสอดคล้องกับ Nothing และ Just ตามลำดับ
      เพียงแต่ฝั่ง Nothing เพิ่มพารามิเตอร์ Unit เข้าไปเพื่อให้แม่นยำขึ้นเล็กน้อย
    • มองได้ว่าเป็นการแปลง inductive type ให้เป็น ชนิดของฟังก์ชัน recursor ของ type นั้น
      ในทฤษฎีชนิดมันค่อนข้างเท่มาก แต่กับการเขียนโปรแกรมจริงกลับไม่ค่อยดีนัก
    • ในเชิงคณิตศาสตร์มันอาจสง่างาม แต่เมื่อถ่ายทอดผ่านภาษาที่มีแค่ ชุดอักขระ ASCII โดยไม่มีการจัดวางหรือเหตุผลรองรับภายใน ก็ดูทรมานตาอยู่พอสมควร
    • พูดสั้น ๆ คือ untyped lambda calculus เป็นระบบที่สมบูรณ์แบบแบบทัวริง
  • ก่อนหน้านี้เคยดูคอร์ส SICP ของจริง คือวิดีโอบันทึกการสอนของ MIT OCW จากปี 1986
    คนมักชมว่ามีความหนาแน่นของข้อมูลสูง แต่เอาเข้าจริงกลับเสียเวลาไปมากพอสมควรกับช่วงถามตอบนักศึกษา, ช่วงที่ผู้สอนต้องดึงความสนใจกลับมาระหว่างที่มีการพยายามนำเสนอ “multimedia” ในห้องเรียน, และช่วงที่ยังป้องกันคำถามล่วงหน้าไม่ได้เพราะแผนการสอนทั้งคอร์สยังไม่ได้จัดวางจนสมบูรณ์ตั้งแต่แรก
    เวลาที่ใช้เขียนบนกระดานเมื่อสะสมรวมกันก็ไม่น้อย
    แน่นอนว่าลำดับเนื้อหานั้นถกเถียงและจัดใหม่ได้เสมอ และสักวันหนึ่งก็มีแผนจะทำวิดีโอชุดที่อธิบายเนื้อหานี้ในแบบที่เข้ากับสไตล์ของตัวเอง
    รู้สึกยินดีที่คอร์สนี้ดูเหมือนจะยังรักษารากเดิมไว้ แม้จะใช้ภาษาใหม่กว่าอย่าง Python และคิดว่าผู้คนยังประเมินความสามารถในการแสดงออกผ่าน สำนวนแบบฟังก์ชันนัล ของ Python ต่ำเกินไป แม้มันจะไม่บริสุทธิ์เต็มตัวก็ตาม เพราะ Python เป็นภาษาหลายกระบวนทัศน์ที่ใช้งานได้จริง

    • คอร์สนี้เริ่มจากการเขียน Scheme ด้วย Python แล้วค่อยเขียน Scheme ด้วย Scheme อีกที
      มองว่า Python ตัดออกจากคอร์สนี้ได้ และควรถูกตัดออกด้วย
      Python รองรับการเขียนโปรแกรมเชิงฟังก์ชันนัลได้อ่อนมาก
      ลิสต์ไม่ได้อิง cons, lambda ก็มีข้อจำกัดมาก, pattern matching ก็แย่มาก แถมยังไม่เป็น expression-based และระบบ namespace ก็ประหลาด
      จะเรียก Python ว่าเป็นภาษาสมัยใหม่ก็ยาก มันเหมือนยังติดอยู่ในยุค 1990s และเติบโตขึ้นมาได้โดยน่าเสียดายที่เบียดบังภาษาอื่นที่ดีกว่า เพียงเพราะมี C-API ที่ดี
    • การเขียนโปรแกรมเชิงฟังก์ชันนัลของ Python ค่อนข้างอ่อน และในแง่นั้นมองว่า JavaScript กลับทำได้ดีกว่า
      เพราะไม่มี tail call optimization แบบสมบูรณ์ แบบฝึกหัดโค้ดบางข้อจึงต้องใช้วิธีแก้ที่ต่างจาก Scheme โดยสิ้นเชิง
      ถ้าโค้ดที่แปลแบบ 1:1 ใช้ไม่ได้ ผู้สอนควรบอกนักศึกษาว่าให้เชื่อไว้ก่อนเพราะมันทำงานแบบนั้นจากภาษาที่เลือกใช้ หรือควรมองทุกอย่างเป็นปัญหาของการย้ายสแตกออกมาภายนอกแล้วแก้แบบนั้นกันแน่
      การยัด SICP ให้เข้ากับ Python แบบฝืน ๆ ดูค่อนข้างโง่เขลา
    • ยังมีคอร์สของ ArsDigita University ด้วย
      ตอนนี้เว็บไซต์ออฟไลน์ไปแล้ว แต่ยังดูคอร์สได้บน archive.org
      https://en.m.wikipedia.org/wiki/ArsDigita#ArsDigita_Foundati...
      https://archive.org/details/arsdigita_01_sicp/
      พวกเขาเคยขาย USB key ที่บรรจุหลักสูตรทั้งหมดไว้ ถ้ามีใครอัป ISO ขึ้นมาจะดีมากจริง ๆ
      https://web.archive.org/web/20190222145553/aduni.org/drives/
    • ปัญหาของ Python และภาษาเกือบทั้งหมดที่ไม่ใช่สาย Lisp คือมันไม่เปิดให้ ปฏิบัติต่อโปรแกรมเหมือนข้อมูล ได้อย่างง่ายดาย
      สิ่งที่เขียนได้ง่ายใน Scheme กลายเป็นแบบฝึกหัดซับซ้อนในภาษาอื่น
      นักศึกษาจึงไปโฟกัสกับรายละเอียดของภาษา implementation ที่ใน Scheme ไม่ต้องสนใจ แทนที่จะโฟกัสกับแนวคิดพื้นฐาน
  • ตอนที่เห็นการ implement cons/car/cdr ด้วย lambda ครั้งแรก รู้สึกเหมือนเวทมนตร์
    แต่สุดท้ายก็มองว่ามันเป็นการแสดงให้เห็นว่า language runtime เองกำลัง implement dictionary แบบคีย์/ค่า อยู่ และเราสามารถอาศัย implementation นั้นมาสร้างโครงสร้างข้อมูลอื่นได้

    • มองว่า ตรรกะการ destructuring ของ Elixir น่าสนใจกว่ามาก และเวอร์ชันที่ลดทอนลงของ ES6 ก็ใช้งานได้จริงกว่ามาก
      ใน Elixir สามารถดึงค่าจากด้านหน้าออกมาได้มากเท่าที่ต้องการ
    • จริง ๆ แล้วไม่จำเป็นต้องมี dictionary แบบคีย์/ค่า แค่มี pointer ก็พอ
      closure ที่ไม่มีพฤติกรรมก็เป็นแค่ pointer ไปยังตัวแปรที่ปิดไว้ และ closure ที่มี pointer 2 ตัวก็คือ pair ที่ใช้เอา car กับ cdr ออกมาได้
      runtime ต้องทำให้สิ่งที่ pointer ชี้อยู่ยังใช้งานได้นอกขอบเขตที่ประกาศไว้ จึงต้องมี escape analysis, garbage collection ฯลฯ แต่ไม่จำเป็นต้องมี dictionary
  • ไม่นานมานี้ได้เจอแนวคิดว่าในการพิสูจน์ทฤษฎีบท ถ้าจะพิสูจน์อะไรอย่าง 0 != 1 จะใช้แค่ Church encoding ไม่พอ แต่ต้องมี inductive data type
    เคยเขียนเรื่องที่เกี่ยวข้องไว้คร่าว ๆ ตรงนี้ และมีคำวิจารณ์ต่อ SICP อีกประเด็นหนึ่งรวมอยู่ด้วย: https://intellec7.notion.site/Drinking-SICP-hatorade-and-why...
    อยากเข้าใจข้อจำกัดของมุมมองแบบ “ทุกอย่างเป็นแค่ฟังก์ชัน” ให้ดีขึ้น

    • ถ้ามีข้อเท็จจริงเชิงรูปธรรมอื่นเกี่ยวกับอสมการที่นำมาใช้ได้ ก็น่าจะพิสูจน์ 0 ≠ 1 ได้
      สามารถอนุมานจากทฤษฎีบท f = g -> f x = g x เพื่อสร้างข้อเท็จจริงเรื่องอสมการทางด้านขวา แล้วค่อยใช้ contrapositive ของมัน
      คำกล่าวที่ว่าอสมการระหว่าง Church numeral พิสูจน์ตรง ๆ ไม่ได้หากไม่มีข้อเท็จจริงอื่นเกี่ยวกับอสมการ ก็ดูจะถูกต้อง
      ในทางกลับกัน สำหรับ inductive data type ระบบพิสูจน์สามารถ “สังเกต” ความเท่ากันหรือความไม่เท่ากัน ได้โดยตรง ผ่านการลอกการใช้ constructor ชั้นนอกสุดของอินสแตนซ์รูปธรรม 2 ตัวที่อยู่ใน inductive type เดียวกันออกแบบเวียนเกิด
    • สำหรับการพิสูจน์ทฤษฎีบท จำเป็นต้องมี Sigma type และ Pi type รวมถึงแนวคิดเรื่องความเท่ากันบางรูปแบบ
      สงสัยว่าสามารถทำสิ่งนั้นได้ด้วย Scott encoding หรือ Church encoding หรือไม่
    • ลิงก์ที่แก้แล้ว: https://goosetaco.notion.site/Drinking-SICP-hatorade-and-why...
  • ตัวหนังสือเองกำลังมีการคุยกันอยู่ที่นี่แล้ว: https://news.ycombinator.com/item?id=42157558
    สงสัยว่ามีเหตุผลอะไรหรือไม่ที่ลิงก์พาไปยังบทสนทนาช่วงล่างแทนที่จะเป็นต้นหน้าของหน้านั้น
    รู้สึกว่าโพสต์นี้น่าจะรวมเข้ากับบทสนทนาที่มีอยู่เดิมได้

  • David Beazley เป็นบุคคลระดับตำนานพอตัวในโลก Python และคอร์สนี้ตอนแรกดูเหมือนไอเดียที่น่าทึ่ง แต่พอคิดดูสัก 2 วินาทีกลับรู้สึกว่าเป็นการจับคู่ที่ลงตัวมาก เลยไปลงเรียนคอร์สถัดไป
    ประเด็นสำคัญคือ รูปแบบแบบนี้น่าจะเป็นหน้าตาของ การศึกษาต่อเนื่องสำหรับวิศวกรรมซอฟต์แวร์ ในอนาคต

    • ถ้าไม่ได้ครอบคลุมบทที่ 5 เรื่อง คอมไพเลอร์ ก็เท่ากับยังไม่ได้แตะส่วนที่ดีที่สุดของ SICP
  • มีพิมพ์ผิดในโค้ดของส่วน “the substitution model”
    ("+", ("fib", ("-", "n", 2)), ("fib", ("-", "n", 1))),
    ที่นิยามไว้คือ fibonacci และไม่ได้มีการนิยาม fib ดังนั้นการเรียก fib ทั้งสองจุดควรเป็น fibonacci อย่างชัดเจน
    โค้ดใน GitHub repository จริงถูกต้อง: https://github.com/savarin/pyscheme/blob/0f47292c8e5112425b5...

  • คิดว่า SICP ยอดเยี่ยมมาก
    แต่ยิ่งเรียนรู้มากขึ้นและศึกษาคณิตศาสตร์มากขึ้น ก็ยิ่งโน้มเอียงไปสู่ข้อสรุปว่า ความสัมพันธ์ เป็นแนวคิดปฐมฐานที่พื้นฐานยิ่งกว่า
    ฟังก์ชันทั้งหมดสามารถแสดงเป็นความสัมพันธ์แบบจำกัดรูปแบบได้ แต่ในทางกลับกันจะทำแบบนั้นไม่ได้หากไม่เพิ่มกลไกเสริมเข้าไปมากพอสมควร
    แม้ฐานข้อมูลเชิงสัมพันธ์และ SQL จะเป็นตัวอย่างของการเขียนโปรแกรมเชิงสัมพันธ์ที่เป็นที่รู้จักและประสบความสำเร็จมากที่สุด แต่ก็ยังมองว่าพื้นที่นี้แทบยังไม่ได้รับการสำรวจอีกมาก
    ตอนนี้ความสนใจใกล้เคียงกับการสอนพื้นฐานคณิตศาสตร์ให้เด็กเล็กมากกว่าการออกแบบภาษาโปรแกรม
    น่าแปลกที่การสอนว่า “ใหญ่” เป็นความสัมพันธ์หนึ่งอาร์กิวเมนต์ และ “ใหญ่กว่า” เป็นความสัมพันธ์สองอาร์กิวเมนต์นั้นง่ายกว่าการพยายามอธิบายแนวคิดเดียวกันในฐานะฟังก์ชันมาก

  • ไม่ค่อยชอบมุมมองแบบ “ทุกอย่างคือฟังก์ชัน” เพราะมันลดทอนความซับซ้อนมากเกินไปและมักไม่ได้ช่วยอะไรนัก
    ตัวอย่างเช่น มีฟังก์ชันที่ไม่สามารถใส่ไว้ในแคช, RAM หรือดิสก์ได้, มีฟังก์ชันที่ Big-O ระเบิด อย่าง N-way JOIN หรือการค้นหา/การจับคู่ และมีฟังก์ชันที่มีผลข้างเคียงรวมถึงความไม่ไอดีมโพเทนต์
    แทบไม่มีใครคิดถึงการโจมตีแบบ side-channel ต่อฟังก์ชัน
    ยังมีฟังก์ชันไม่กำหนดแน่ชัดที่ขึ้นอยู่กับวันที่ เวลา และระยะเวลา และฟังก์ชันก็อาจล้มเหลวกลางทางหรือไม่ล้มเหลวอย่างสวยงามก็ได้
    และก็ยากที่จะสมมติว่ามันไม่ใช้ทรัพยากรจนไปกระทบ “ฟังก์ชัน” อื่นที่ใช้พูลทรัพยากรร่วมกัน
    อาร์กิวเมนต์ของฟังก์ชันอาจมีขนาดใหญ่หรือซับซ้อนได้ไม่จำกัด แต่ในโลกจริงมีขีดจำกัด และเมื่อนั้นก็ต้องใช้พอยน์เตอร์ แล้วสุดท้ายก็ต้องมีการอ้างอิงระยะไกลอย่างเว็บหรือดิสก์อีก
    อยากให้มีคนบอกให้หยุดได้แล้ว เพราะพูดต่อได้เรื่อย ๆ

    • การลดทอนความซับซ้อนมากเกินไปบางครั้งก็ยอดเยี่ยมได้
      ในกรณีนี้ แบบจำลองแลมบ์ดาแคลคูลัส คือรากฐานของแนวทาง “ทุกอย่างก็แค่ฟังก์ชัน” และเป็นแบบจำลองการคำนวณที่ดี เพราะเรียบง่ายมากและจัดการรวมถึงให้เหตุผลได้ง่ายกว่าเมื่อเทียบกับเครื่องทัวริงและสิ่งอื่น ๆ
      จึงเป็นรากฐานของตรรกะคอมพิวเตอร์และระบบพิสูจน์ส่วนใหญ่
    • หลายอย่างในนั้นสามารถจำลองเป็นฟังก์ชันได้ และในความเป็นจริงก็แค่ต้องเขียนให้เป็นแบบนั้น
      ถ้าฟังก์ชันใดต้องใช้ทรัพยากร ก็ทำให้มันต้องรับทรัพยากรนั้นเข้าไป ถ้ามันขึ้นกับวันที่/เวลา ก็ทำให้มันขึ้นกับวันที่/เวลา และถ้ามันคืนค่า ค่าที่ไม่กำหนดแน่ชัด ก็ทำให้มันคืนค่าที่ไม่กำหนดแน่ชัดไปเลย
      หนึ่งในเหตุผลที่แนวทางการเขียนโปรแกรมเชิงฟังก์ชันโดดเด่นก็คือ มันบังคับให้ต้องจัดการเรื่องพวกนี้อย่างจริงจัง
      ถ้าอยากใช้ทรัพยากรที่ถูกแชร์แบบแฝง ก็ต้องจำลองมันขึ้นมา และ “ฟังก์ชัน” ที่พึ่งพาทรัพยากรแชร์แบบแฝงก็จะแตกต่างจากฟังก์ชันจริงอย่างชัดแจ้ง
    • ครึ่งหนึ่งของสิ่งที่ถูกเรียกว่าฟังก์ชันตรงนี้ แท้จริงแล้วไม่ใช่ฟังก์ชัน และแม้แต่ในโลกของการเขียนโปรแกรมเชิงฟังก์ชันเอง หลายคนก็คงไม่เรียกมันว่าฟังก์ชัน
      มันใกล้เคียงกับ กระบวนวิธี มากกว่า
      ฟังก์ชันเป็นกระบวนวิธีชนิดหนึ่ง แต่ไม่ใช่ว่ากระบวนวิธีทุกอย่างจะเป็นฟังก์ชัน
    • สำหรับจุดประสงค์เชิงทฤษฎี มองว่านั่นคือความหวังเดียว