1 คะแนน โดย GN⁺ 2025-04-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • แกนสำคัญที่ทำให้ Erlang สร้าง ระบบกระจายศูนย์ ที่เชื่อถือได้ ไม่ได้อยู่ที่ โปรเซสเบา โดยตัวมันเอง แต่อยู่ที่ behaviours ซึ่งทำให้แพตเทิร์นซ้ำ ๆ ของการจัดการ concurrency และความล้มเหลวกลายเป็นสิ่งทั่วไป
  • behaviours ให้จุดสำหรับ implement คล้าย interface แต่ก็ซ่อนการจัดการ concurrency ไว้ในคอมโพเนนต์ส่วนกลางอย่าง gen_server ทำให้โค้ดแอปพลิเคชันใกล้เคียงกับตรรกะแบบลำดับมากขึ้น
  • วิทยานิพนธ์ของ Joe Armstrong กล่าวถึงกรณีที่สร้างระบบขนาดใหญ่อย่าง AXD301 ด้วยคอมโพเนนต์เล็ก ๆ อย่าง gen_server, gen_event, gen_fsm, supervisor, application, release
  • supervisor รีสตาร์ตโปรเซสที่ล้มเหลวด้วยกลยุทธ์อย่าง one_for_one, one_for_all และปรัชญา “Let it crash!” ย้ายความรับผิดชอบในการกู้คืนไปไว้ที่ supervisor tree
  • behaviours ในรูปแบบ state machine ทำให้ การทดสอบแบบจำลองสถานการณ์ และการตรวจสอบเชิงรูปแบบทำได้ง่ายขึ้น และทำให้นักพัฒนาโฟกัสที่ semantics ของปัญหา มากกว่าความซับซ้อนของ concurrency และ simulator

ปัญหาที่ Erlang ต้องการแก้

  • Erlang เริ่มต้นในฐานะเครื่องมือสำหรับสร้าง ระบบกระจายศูนย์ ที่เชื่อถือได้
    • ตอนแรกเป็นไลบรารี Prolog สำหรับสร้างระบบกระจายศูนย์ที่เชื่อถือได้ จากนั้นผ่านการเป็นภาษา dialect ของ Prolog ก่อนจะกลายเป็นภาษาอิสระ
    • ถูกใช้เขียนโปรแกรมตู้ชุมสายโทรศัพท์ของ Ericsson และในช่วงทศวรรษ 80–90 ต้องรองรับทราฟฟิกระดับผู้ใช้หลายร้อยล้านคนพร้อม SLA ที่เข้มงวด
  • Ericsson สั่งห้ามใช้ Erlang ในปี 1998 และทีมพัฒนาก็โต้แย้งว่า หากจะห้ามใช้ก็ควรเปิดเป็นโอเพนซอร์สเสียเลย ซึ่ง Ericsson ก็ทำตามนั้น
  • Joe Armstrong เป็นหนึ่งในบุคคลสำคัญด้านการออกแบบและการ implement Erlang และในปี 2002 เขาเริ่มเขียนวิทยานิพนธ์ปริญญาเอกที่ SICS ก่อนจะเสร็จในปี 2003 ในชื่อ Making reliable distributed systems in the presence of software errors
  • วิทยานิพนธ์นี้เน้นแนวคิดที่อยู่เบื้องหลัง Erlang และประสบการณ์ในการสร้างระบบกระจายศูนย์ที่เชื่อถือได้ มากกว่าคณิตศาสตร์หรือทฤษฎี
  • ไอเดียใหญ่ของ Erlang ไม่ใช่ โปรเซสเบาและการส่งข้อความ แต่เป็นคอมโพเนนต์ทั่วไปที่ Erlang เรียกว่า behaviours

behaviours: การผสาน interface กับ infrastructure

  • Erlang behaviours คือชุดของ type signature ที่สามารถมี implementation ได้หลายแบบ คล้าย interface ใน Java หรือ Go
  • เมื่อโปรแกรมเมอร์จัดเตรียม implementation ของ interface นั้นแล้ว ก็สามารถใช้ฟังก์ชันทั่วไปที่เขียนขึ้นสำหรับ interface นั้นได้
  • ความต่างสำคัญคือ behaviours ไม่ได้เป็นเพียง interface แต่ยังมาพร้อม โค้ด infrastructure ด้วย
    • โปรแกรมเมอร์แอปพลิเคชันเขียน semantics ของปัญหา หรือก็คือ business logic
    • โค้ด infrastructure อย่าง concurrency ถูก behaviour จัดเตรียมให้โดยอัตโนมัติ
    • behaviours บรรจุแนวปฏิบัติที่ดีซึ่งเขียนโดยผู้เชี่ยวชาญมากประสบการณ์
    • หากทั้งระบบ reuse ชุด behaviour ขนาดเล็ก เมื่อ implementation ของ behaviour ดีขึ้น ระบบก็สามารถดีขึ้นได้โดยไม่ต้องแก้โค้ด
    • การใช้ behaviour บังคับให้มีโครงสร้าง จึงทำให้การทดสอบและการตรวจสอบเชิงรูปแบบง่ายขึ้น

gen_server: concurrency ที่ซ่อนอยู่หลังโค้ดแบบลำดับ

  • ตัวอย่าง gen_server เป็น key-value store โดย store ใช้เก็บคู่ key-value และ lookup ใช้ค้นหาค่าของ key
  • callback หลักอย่าง handle_call อัปเดตสถานะ Dict สำหรับคำขอ store และค้นหา key จากสถานะสำหรับคำขอ lookup
  • เมื่อนำ implementation นี้ไปให้ gen_server ก็จะได้เซิร์ฟเวอร์ที่สามารถจัดการคำขอ store และ lookup ที่เข้ามาพร้อมกันได้
  • จุดสำคัญคือ handle_call เองนั้น เป็นแบบลำดับอย่างสมบูรณ์
    • concurrency ถูกซ่อนไว้ภายในคอมโพเนนต์ gen_server ทั่วไป
    • โค้ดแอปพลิเคชันใกล้เคียงกับรูปแบบที่รับสถานะและอินพุต แล้วสร้างสถานะใหม่และเอาต์พุต
  • วิทยานิพนธ์ของ Joe Armstrong ขยายแนวทางนี้ไปถึง gen_server, gen_event, gen_fsm, supervisor, application, release

กรณี AXD301 และขนาดของ behaviours

  • Joe Armstrong ใช้ตู้ชุมสายโทรศัพท์ AXD301 ของ Ericsson เป็นกรณีศึกษา
  • โปรเจกต์ AXD301 มีองค์ประกอบดังต่อไปนี้
    • อินสแตนซ์ gen_server 122 ตัว
    • อินสแตนซ์ gen_event 36 ตัว
    • อินสแตนซ์ gen_fsm 10 ตัว
    • supervisor 20 ตัว
    • application 6 ตัว
    • ทั้งหมดถูกแพ็กเกจเป็น release เดียว
  • AXD301 เป็นระบบที่มีโค้ด Erlang มากกว่า 1 ล้านบรรทัด
  • การที่ระบบขนาดนี้ถูกประกอบขึ้นด้วยชุด behaviour ขนาดเล็ก กลายเป็นหลักฐานสำคัญของโครงสร้าง Erlang

บทบาทของ behaviours อื่น ๆ

  • gen_event คือ event manager ทั่วไป
    • ลงทะเบียน event handler และเมื่อ event manager ได้รับข้อความที่เกี่ยวข้อง ก็จะเรียกใช้ handler
    • Joe Armstrong ยกตัวอย่างการใช้งานอย่าง error logging
    • logger ตัวอย่างสามารถบันทึกและรายงานข้อความผิดพลาดล่าสุดห้ารายการได้
  • gen_fsm คือ behaviour แบบ state machine ซึ่งภายหลังเปลี่ยนชื่อเป็น gen_statem
    • เหมาะกับการ implement protocol มากกว่า
    • protocol มักถูกระบุในรูป state machine
    • มองได้ว่า gen_server ใด ๆ ก็สามารถ implement ด้วย gen_statem ได้ และกลับกันก็ได้เช่นกัน
  • application ประกอบด้วย supervisor tree และองค์ประกอบที่เหลือซึ่งจำเป็นต่อการส่งมอบแอปพลิเคชัน
  • release แพ็กเกจ application อย่างน้อยหนึ่งตัว
    • รวมโค้ดจัดการ upgrade ด้วย
    • หาก upgrade ล้มเหลว ต้องสามารถ rollback กลับไปยังสถานะเสถียรก่อนหน้าได้

supervisor และ “Let it crash!”

  • supervisor คือโปรเซสที่ตรวจสอบว่าโปรเซสอื่นทำงานตามปกติหรือไม่
  • หากโปรเซสที่ถูกเฝ้าระวังล้มเหลว supervisor สามารถรีสตาร์ตได้ตามกลยุทธ์ที่กำหนดไว้ล่วงหน้า
  • กลยุทธ์ {one_for_one, 5, 1000} ในตัวอย่างหมายถึงสิ่งต่อไปนี้
    • หากหนึ่งใน packet_assembler, kv, simple_logger ล้มเหลว จะรีสตาร์ตเฉพาะโปรเซสที่ล้มเหลวเท่านั้น
    • หากต้องรีสตาร์ตมากกว่า 5 ครั้งในช่วง 1000 วินาที ตัว supervisor เองจะล้มเหลว
  • permanent, 500, worker หมายถึงโปรเซสนั้นเป็น worker ที่ต้องมีชีวิตอยู่เสมอ และเมื่อ supervisor พยายามรีสตาร์ต จะให้เวลา 500ms เพื่อให้ปิดตัวอย่างปกติ
  • ในกลยุทธ์ one_for_all หากโปรเซสหนึ่งล้มเหลว จะรีสตาร์ตโปรเซสลูกทั้งหมด
  • supervisor สามารถเฝ้าระวัง supervisor อื่นได้ และไม่จำเป็นต้องรันอยู่บนคอมพิวเตอร์เครื่องเดียวกัน
  • โครงสร้างนี้ทำงานในระดับ เธรด/โปรเซสเบา ไม่ใช่ระดับ Docker container ดังนั้นจึงมองว่าเป็น “ก็แค่ Kubernetes” ได้ยาก
  • “Let it crash!” ตั้งอยู่บนสมมติฐานว่าโปรเซสที่ล้มเหลวจะถูกรีสตาร์ต
    • โปรแกรมแสดงเฉพาะเส้นทางปกติ
    • หากเกิดปัญหาในเส้นทางปกติ ก็ crash แทนที่จะพยายามกู้คืนเอง
    • โปรแกรมอื่นที่อยู่สูงกว่าใน supervisor tree จะรับช่วงจัดการต่อ
  • หลักฐานเรื่องเสถียรภาพระยะยาวของ AXD301 ไม่ได้ถูกรวบรวมอย่างเป็นระบบ แต่ในวิทยานิพนธ์ของ Joe Armstrong ระบุว่ามีตัวเลขจาก PowerPoint ว่าลูกค้ารายใหญ่รายหนึ่งรันระบบ 11 โหนดด้วย ความน่าเชื่อถือ 99.9999999%
    • ไม่มีเอกสารระบุว่าตัวเลขดังกล่าวได้มาอย่างไร
    • ตัวเลขจากรายงาน downtime อื่น ๆ ก็มีหมายเหตุว่าขั้นตอนการคำนวณไม่ชัดเจนเช่นกัน

หาก implement behaviours ในภาษาอื่น

  • โปรเซสเบาและการส่งข้อความเพียงอย่างเดียวไม่ได้ทำให้ Erlang เหมาะกับระบบที่เชื่อถือได้
  • คำอธิบายที่ตรงไปตรงมามากกว่าคือ โครงสร้าง ที่ behaviours มอบให้ และวิธีที่โครงสร้างนั้นนำไปสู่ซอฟต์แวร์ที่เชื่อถือได้
  • interface signature ของ gen_server สามารถมองได้ในรูปแบบต่อไปนี้
    • Input -> State -> (State, Output)
    • หมายถึงรับอินพุตและสถานะปัจจุบัน แล้วสร้างสถานะใหม่กับเอาต์พุต
  • วิธีเปลี่ยน signature แบบลำดับนี้ให้รองรับคำขอพร้อมกัน สามารถคิดได้ดังนี้
    • HTTP server แปลงคำขอเป็น Input แล้วใส่ลงใน queue
    • event loop ดึงอินพุตออกจาก queue แล้วส่งให้ implementation แบบลำดับ
    • นำเอาต์พุตกลับไปใช้เป็น response ให้ client
    • หากต้องรองรับ gen_server หลายตัว ก็สามารถตั้งชื่อให้แต่ละ server และแนบชื่อกับอินพุตไว้ในคำขอได้
  • gen_event สามารถ implement ได้ด้วยการอนุญาตให้ลงทะเบียน callback กับ event type บางชนิดใน queue
  • supervisor สามารถทำให้เรียบง่ายโดยครอบการเรียกฟังก์ชัน gen_server ด้วย exception handler และหากเกิด exception ก็แจ้ง supervisor
    • หาก supervisor ไม่ได้รันบนคอมพิวเตอร์เครื่องเดียวกัน เรื่องจะซับซ้อนขึ้น
  • application และ release สำคัญเพราะ configuration, deployment และ upgrade เป็นปัญหาที่ยาก แต่ไม่ได้ลงรายละเอียดมากนัก

behaviours และความสามารถในการทดสอบ

  • โครงสร้างของ Erlang behaviours นำไปสู่ การทดสอบแบบจำลองสถานการณ์ และการตรวจสอบเชิงรูปแบบ
  • ความสนใจล่าสุดคือการทดสอบแบบจำลองสถานการณ์ของระบบกระจายศูนย์สไตล์ FoundationDB
  • การทดสอบแบบจำลองสถานการณ์คือการรันระบบในโลกจำลอง โดยตัวจำลองควบคุมเวลาส่งข้อความผ่านเครือข่ายได้อย่างสมบูรณ์
  • FoundationDB สร้าง dialect ของ C++ ที่มี actor หรือภาษาโปรแกรมของตนเองเพื่อการทดสอบแบบจำลองสถานการณ์
  • มองว่าแนวทางนี้สามารถไปได้ไกลพอสมควรด้วย state machine type ต่อไปนี้เท่านั้น
    • Input -> State -> (State, [Output])
    • [Output] คือ sequence ของเอาต์พุต
  • simulator จัดการข้อความด้วย priority queue ตามเวลาที่มาถึง
    • ดึงข้อความหนึ่งรายการออกมา
    • เลื่อนนาฬิกาไปยังเวลามาถึงของข้อความนั้น
    • ส่งข้อความให้ state machine ฝั่งรับ
    • สร้างเวลามาถึงใหม่ให้ข้อความเอาต์พุต แล้วใส่กลับเข้า queue
  • หากทุกอย่าง deterministic และเวลามาถึงถูกสร้างจาก seed ก็สามารถสำรวจ interleaving หลายรูปแบบและได้ failure ที่ reproduce ได้
  • วิธีนี้เร็วกว่า Jepsen เพราะข้อความถูกประมวลผลในหน่วยความจำ และนาฬิกาถูกเลื่อนไปยังเวลามาถึงโดยไม่ต้องรอ timeout
  • รูปแบบ state machine เช่นนี้ถูกเรียกว่า “network normal form” และนำไปสู่การคาดเดาว่าโปรแกรมทั้งหมดที่รับ-ส่งผ่านเครือข่ายน่าจะ refactor ให้อยู่ในรูปแบบนี้ได้
  • การที่ gen_server และ gen_statem มีโครงสร้าง type แทบเหมือนกัน สามารถมองได้ว่าโครงสร้างนี้ไม่ได้เป็นสิ่งตามอำเภอใจ

การตรวจสอบเชิงรูปแบบและโครงสร้าง state machine

  • Joe Armstrong เคยกล่าวใน talk ว่าการ implement distributed leader election ให้ถูกต้องนั้นทำได้ยาก
  • หากมี simulator ปัญหานี้จะง่ายลงมาก
    • เหมือนกับที่อุโมงค์ลมช่วยในการสร้างเครื่องบิน สามารถทดสอบเงื่อนไขสุดขั้ว เช่น เครือข่ายไม่เสถียรหรือไฟดับ ก่อน production ได้
  • simulator นี้สามารถเป็นแบบทั่วไปหรือแบบมีพารามิเตอร์สำหรับ behaviours ได้
    • นักพัฒนาไม่จำเป็นต้องเขียน simulator แยกเอง
    • ความซับซ้อนถูกซ่อนไว้หลัง behaviour เช่นเดียวกับโค้ด concurrency ของ gen_server
  • เกี่ยวกับการทดสอบของ FoundationDB นั้น Kyle “aphyr” Kingsbury เคยทวีตในทำนองว่าการทดสอบของ FoundationDB ดูเข้มงวดกว่าการทดสอบ Jepsen ของเขามาก
  • การตรวจสอบเชิงรูปแบบก็ง่ายขึ้นหากโปรแกรมถูกเขียนเป็น state machine
    • งาน model checking ของ Lamport ใน TLA+ สมมติว่า specification เป็น state machine
    • Kleppmann แสดงวิธีจัดการปัญหา state explosion ด้วย structural induction ที่ใช้โครงสร้าง state machine
  • โครงสร้างที่ได้จาก Erlang behaviours สามารถถูกนำกลับมาใช้เพื่อแก้ปัญหาที่ Joe Armstrong เคยบอกว่ายากให้ง่ายขึ้น

งานที่เกี่ยวข้องและแหล่งอ้างอิง

  • งานที่เกี่ยวข้องซึ่งนำไอเดียจาก Erlang มีดังนี้
    • งานที่นำไอเดียจาก LMAX Disruptor ของ Martin Thompson และ aeron มาสร้าง event loop ที่รวดเร็วสำหรับรัน behaviours
    • งานที่เพิ่ม async I/O ให้กับ state machine type
    • งานที่ implement supervisors ให้ละเอียดขึ้น
    • swapping โค้ดแบบ hot code ของ state machine
  • แหล่งอ้างอิงที่ระบุไว้ ได้แก่ วิทยานิพนธ์ปริญญาเอก ของ Joe Armstrong, OTP design principles และเอกสารของ gen_server, gen_event, gen_statem, supervisor, application, release
  • ในเรื่องความสัมพันธ์ระหว่าง actor model กับ Erlang สรุปไว้ว่า นักพัฒนา Erlang สร้าง Erlang ขึ้นมาโดยยังไม่รู้จัก actor model และบทความของ Carl Hewitt ได้บันทึกความแตกต่างระหว่างโปรเซส Erlang กับ actor model
  • Akka มี “actors” และ supervisor trees แต่ดูเหมือนไม่มีสิ่งที่เทียบเท่า Erlang behaviours อื่น ๆ และแนวคิด “behavior” ของ Akka ก็แตกต่างจาก Erlang behaviours

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

 
GN⁺ 2025-04-13
ความคิดเห็นบน Hacker News
  • สิ่งที่น่าทึ่งของ Erlang และ BEAM คือความลึกของฟีเจอร์ต่าง ๆ ในบทความต้นฉบับ Behaviour/Interface เป็นความประทับใจหลัก แต่สำหรับผม สิ่งที่สำคัญกว่านั้นคือเมื่อสร้างระบบที่ซับซ้อน มันทำได้ด้วยทรัพยากรการพัฒนาน้อยกว่าภาษาอื่นมาก
    ใน OTP เองก็มีอะไรอยู่มากมาย ผมเคยลองคอมไพล์ให้ Elixir รันบนอุปกรณ์ iOS และไม่ใช่แค่กระบวนการ release ของ Erlang เท่านั้น แต่ยังใช้ ไลบรารี ei เพื่อคอมไพล์ Node ด้วย C และสามารถสื่อสารกับโหนด Erlang อื่น ๆ เช่น Erlang, Elixir, Gleam ฯลฯ ได้เหมือนเป็นเครือข่ายแบบกระจายทั่วไป
    นอกจากนี้ยังสามารถเรียกฟังก์ชันของแอปพลิเคชัน Elixir จาก C ผ่านไลบรารี rpc ของ Erlang ได้ด้วย มี overhead ในการเข้ารหัส/ถอดรหัส และ FFI น่าจะเร็วกว่า แต่ก็ยังอยู่ในงบ latency ได้สบาย และผมสามารถตั้งฟีเจอร์ที่ไม่เคยได้ยินมาก่อนขึ้นมาได้ภายในไม่กี่วัน
    ประเด็นที่ใหญ่กว่าคือ Erlang ได้แก้ปัญหาหลายอย่างที่เทคสแต็กสมัยใหม่กำลังต่อสู้อยู่ไปแล้วตั้งแต่หลายสิบปีก่อน ทั้งในแง่สเกลและต้นทุนการนำไปใช้ แม้ HN จะมีความรักต่อ Erlang/Elixir แบบชวนคลิกอยู่บ้าง แต่ก็ไม่ได้แปรเป็นการนำไปใช้จริง และมีบริษัทที่กำลังเผาเงินเพื่อสร้างสิ่งที่ได้มาฟรีเป็นพื้นฐานในสแต็ก Erlang

    • ผมย้ายจากบริษัทที่ใช้แบ็กเอนด์ Elixir ไปยังบริษัทที่ใช้ Node.js เดิมทีแทบไม่เคยใช้ Node.js เลย จึงมองอย่างเป็นกลาง
      โปรเจกต์ที่ทำเป็น data pipeline ฝั่งแบ็กเอนด์ และข้อมูลที่ประมวลผลก็ไม่ได้มาก แต่การแยกบั๊กหลักให้ชัดเจนกลับยากจนน่าเหลือเชื่อ
      ระหว่างนั้นผมได้รู้จักคุณสมบัติหลายอย่างของ Node.js และเมื่อเทียบกับ Elixir/Erlang/OTP ก็ได้ข้อสรุปว่า Node.js มีความน่าเชื่อถือต่ำโดยการออกแบบ
      ผมก็ทำ Ruby มามาก และเคยแตะ Python ด้วย แต่แพลตฟอร์มภาษารุ่นปัจจุบันจำนวนมากยังมีปัญหาในการสร้างระบบกระจายที่เชื่อถือได้ ส่วน BEAM VM และแพลตฟอร์ม OTP แก้ส่วนนี้ได้แล้ว
    • องค์ประกอบพื้นฐาน ที่ Erlang และลูกหลานของมันให้มานั้นจัดการง่าย และจึงทดสอบได้ง่ายด้วย
      ตัวอย่างเช่น GenServer ซึ่งทำหน้าที่เป็น worker ในระบบ BEAM ส่วนใหญ่ ท้ายที่สุดก็เป็นโครงสร้างที่เรียกฟังก์ชันหลายตัวด้วยพารามิเตอร์ง่าย ๆ
      ดังนั้นเราจึงทดสอบได้ด้วยการเรียกฟังก์ชันเหล่านั้นโดยตรง ส่งพารามิเตอร์ด้วยมือ แล้วตรวจสอบแค่ผลลัพธ์ ไม่จำเป็นต้องตั้งระบบทดสอบซับซ้อนเพื่อจัดการโค้ดอะซิงโครนัส หรือหยุดรอให้งานเสร็จในการทดสอบ
      เป็นจุดที่ junior มักพลาด แต่พอเข้าใจแล้วก็ให้ความรู้สึกปลดล็อกมาก
    • C node ถูกประเมินค่าต่ำไป เราใช้ C node ที่อิง Cgo สำหรับการสื่อสารระหว่างบริการ Go และ Elixir ที่รันอยู่ใน Kubernetes pod เดียวกัน เอกสารของ Erlang และไลบรารี C ก็ค่อนข้างดีด้วย
    • อยากรู้ว่าชุมชนมองเหตุผลอย่างไรที่ HN ดูเหมือนจะชอบ Erlang/Elixir แต่กลับไม่ได้นำไปสู่การใช้งานจริง
  • เคยเห็นบางคน ส่วนใหญ่เป็นผู้จัดการ บอกว่าจะเขียนหนังสือจากประสบการณ์ของเรา สิ่งที่ทำให้หงุดหงิดเสมอคือแต่ละคนมองว่าส่วนที่เป็นหัวใจของความสำเร็จของงานเราต่างกัน สิ่งที่ผมรู้สึกว่าจำเป็น พวกเขามักลดทอนให้เป็นแค่ของที่มีก็ดี
    ในที่นี้ก็เช่นกัน มีคนบอกว่า โปรเซสแบบเบาและการส่งข้อความ ไม่ใช่สูตรลับ แต่กลับพลาดไปว่ามุมมองที่มอง Erlang เป็นกระบวนการลำดับที่สื่อสารกัน (CSP) นั้นแยกออกจากคุณลักษณะของมันไม่ได้ ถึงอย่างนั้นก็ยังกล่าวถึง CSP ซ้ำ ๆ ว่าเป็นส่วนหนึ่งของสูตรลับ
    เช่น โปรแกรมเมอร์แอปพลิเคชันเขียนโค้ดแบบลำดับ ส่วน concurrency ถูกซ่อนอยู่ใน behaviour, หรือ business logic เป็นแบบลำดับจึงทำให้สมาชิกทีมใหม่เริ่มต้นได้ง่าย, หรือ supervisor กับปรัชญา “let it crash” สร้างระบบที่เชื่อถือได้ เป็นต้น
    Behaviour น่าสนใจ พบได้ทั่วไปในยุค 80 และช่วยแก้ปัญหาที่บางกลุ่มยังแก้กันในยุค 2000 แต่ใน Erlang มันเป็นทั้งเป้าหมายและเครื่องมือ มันเป็นวิธีที่ใช้สร้างคุณลักษณะอื่น ๆ เหล่านั้น แต่ผมไม่แน่ใจว่า Erlang จำเป็นต้องทำแบบนั้นเพื่อยังคงเป็น Erlang หรือไม่

    • Erlang ไม่ใช่ CSP แต่เป็น actor model https://en.wikipedia.org/wiki/Actor_model
      CSP เป็นสายที่ผ่าน occam และภาษาอื่น ๆ แล้วไปเป็นแรงบันดาลใจให้ channel ของ Go ความแตกต่างที่เด่นที่สุดคือวิธีซิงโครไนซ์บน channel แบบไม่มีบัฟเฟอร์ และยังมีความแตกต่างอย่าง pattern matching กับ mailbox ใน actor model ด้วย
      การถกเถียงระหว่าง CSP กับ actor model นั้นดูเผิน ๆ คล้ายกัน แต่ความหมายเชิงปฏิบัติจริงต่างกันมาก จึงค่อนข้างน่าสนใจ
    • มอง Erlang เป็น CSP กันหรือ? ผมไม่เคยมองแบบนั้น และคิดว่าเป็นโมเดลของตัวเองที่ actor ซึ่งมี identifier สื่อสารกันโดยตรง ผมมองว่า CSP ใกล้กับโปรเซสไม่ระบุตัวตนที่ใช้การส่งข้อความผ่าน channel มากกว่า
      ผมคิดว่า actor model สมเหตุสมผลกว่า แต่ก็อาจแตกต่างกันมากแล้วแต่คน
    • ผู้จัดการจะสร้างเรื่องเล่าของตัวเองให้เข้ากับบรรยากาศ
  • ผมเข้ามาเพราะอยากรู้มากขึ้นว่าทำไม Ericsson ถึงหยุดใช้ Erlang และทำไม Joe ถึงถูกเลิกจ้าง
    ถ้าดูแบบสั้น ๆ Ericsson ย้ายโปรเจกต์ใหม่ไปใช้ Java และดูเหมือนว่านั่นทำให้ Erlang ถูกดันไปอยู่ชายขอบ หลังจากนั้น Joe กับเพื่อนร่วมงานก่อตั้ง Bluetail ในปี 1998 และถูก Nortel ซื้อกิจการ
    Nortel เป็นยักษ์ใหญ่ด้านโทรคมนาคมที่มีมูลค่าประมาณหนึ่งในสามของตลาดหลักทรัพย์โตรอนโต ในปี 2000 ราคาหุ้นขึ้นไปถึง 125 ดอลลาร์ต่อหุ้น แต่ในปี 2002 ตกลงมาต่ำกว่า 1 ดอลลาร์ เป็นส่วนหนึ่งของฟองสบู่ดอตคอมแตก และเมื่อผนวกกับการใช้จ่ายด้านโทรคมนาคมที่ลดฮวบ Nortel จึงโดนหนักเป็นพิเศษ
    ดูจะปลอดภัยกว่าที่จะมองการเลิกจ้างของ Joe ว่าเป็นสถานการณ์ที่ “แผนกของเขาจมน้ำก่อนบนเรือที่กำลังจะจม” Nortel เลิกจ้างคน 60,000 คน หรือมากกว่าสองในสามของพนักงานทั้งหมด การเลิกจ้างนั้นไม่ใช่สัญญาณว่า Joe ทำหน้าที่ได้ไม่ดี และก็ไม่ใช่สิ่งที่แสดงถึงความไม่มีประสิทธิภาพของหน่วยธุรกิจ แต่เป็นมาตรการขนาดใหญ่ที่สิ้นหวัง

    • การเห็นคำว่า “fired” ในบริบทนี้รู้สึกแปลกมาก “laid off” เหมาะสมกว่า
      “fired” มีการตัดสินคุณค่าแรง และให้ความหมายแฝงว่าถูกไล่ออกด้วยสาเหตุเพราะทำผิด แม้จริง ๆ จะเป็นอย่างนั้น ผู้เขียนบทความต้นฉบับก็ไม่มีทางรู้ และไม่ใช่เรื่องที่ควรรู้ด้วย
  • ผมเริ่มกลับมามอง Erlang อีกครั้งเพราะ lightweight process และ message passing และจนถึงตอนนี้ behaviour ยังเป็นเรื่องรอง
    โปรเจกต์นี้คือการนำ flow-based programming (FBP) แบบเชิงภาพมาไว้บน Erlang ดูเหมือน FBP จะถูกสร้างมาเพื่อ Erlang เลย จึงแปลกใจที่ไม่เห็น implementation ที่มีอยู่แล้ว
    เครื่องมือที่ผมใช้กับ FBP เป็นหลักคือ Node-RED ดังนั้นไอเดียพื้นฐานคือเอา frontend ของ Node-RED ไปต่อกับ backend ของ Erlang และทำให้ทุก node เป็น process frontend ของ Node-RED เหมาะกับการ model การส่ง message ระหว่าง node จึงสามารถ map แบบ 1:1 ไปยัง process และ message ของ Erlang ได้อย่างเรียบง่ายมาก
    ผม implement ฟีเจอร์พื้นฐานบางส่วนแล้ว และเริ่มทำ flow เป็น unit test เพื่อค่อย ๆ เพิ่มความสามารถเข้าไป หวังว่าจะเข้ากันได้ 100% กับ Node-RED ซึ่งเป็น backend แบบ Node.js รายละเอียดเพิ่มเติมอยู่ใน GitHub repository → https://github.com/gorenje/erlang-red
    โดยรวมแล้ว Erlang เหมาะกับเรื่องนี้อย่างน่าทึ่ง และผมแปลกใจว่าทำไมไม่มีใครทำอะไรคล้าย ๆ กัน หรือจริง ๆ แล้วเคยมีอยู่แล้ว?
    [1] = https://jpaulm.github.io/fbp/index.html

    • ผมเคยคิดมาตลอดว่าภาษาโปรแกรมมิงแบบเชิงภาพบน BEAM น่าจะสนุกเสมอ
    • มองในระดับสูง อยากรู้ว่าจะถือว่า FBP คล้ายกับ Erlang process ที่ message flow เป็นทิศทางเดียวได้ไหม
  • สำหรับผม พลังของ Erlang/Elixir ไม่ได้อยู่ที่การ implement actor model, matching ที่มาจาก Prolog, immutability หรือ behaviour เอง แต่อยู่ที่ความตั้งใจของ Joe ที่อยากแสดงให้เห็นว่า ทำได้มากขึ้นด้วยสิ่งที่น้อยลง
    มันเป็นระบบคำนวณที่ออกแบบและพิสูจน์มาอย่างดี และมีความสอดคล้องที่หาได้ยากทั้งในภาษาอื่น ๆ และในโลก “เว็บ” แม้จะไม่สมบูรณ์แบบ แต่ก็น่าประทับใจมาก
    น่าเสียดายที่ในโลกซอฟต์แวร์ ผมมองว่าการยอมรับและการนำสิ่งที่ความเรียบง่ายทำให้เป็นไปได้ไปใช้นั้นยังมีน้อยมาก ความซับซ้อนทำให้ผู้คนกลายเป็นผู้เชี่ยวชาญ ทำให้ผู้จัดการมีเหตุผลรองรับทีมใหญ่และการประชุมจำนวนมาก และทำให้ผู้เชี่ยวชาญยังคงเป็นผู้เชี่ยวชาญต่อไป
    Erlang ถูกพัฒนาขึ้นในยุคที่บริษัทต่าง ๆ พยายามสร้าง solution ซอฟต์แวร์ด้วยคนที่น้อยลงและประสิทธิภาพที่จำกัด หลังจากนั้นหลายสิบปี เงินจำนวนมากหลั่งไหลเข้ามาในวงการนี้ ทำให้คุณค่าแบบ “สิ่งที่น้อยกว่าหมายถึงสิ่งที่มากกว่าในแบบที่ดีต่อทุกคน” ดูน่าดึงดูดน้อยลง

    • Alan Kay เคยบอกว่าความเรียบง่ายได้มาจากการเลือก building block ที่ซับซ้อนขึ้นเล็กน้อย Erlang ดูเหมือนจะทำเช่นนั้น
    • นึกถึงบรรยาย Simple VS Easy ของ Rich Hickey
    • คำพูดนี้ทำให้ผมอยากใช้เวลากับ Erlang มากขึ้น ผมลองจับมาบ้างนิดหน่อย และอย่างน้อยในระดับผิวเผิน ผมชอบ syntax ของ Erlang มากกว่า Elixir
  • แนวคิดที่น่าสนใจที่สุดใน Erlang/BEAM คือ partial recovery ถูกฝังมาให้ตั้งแต่ต้น
    เมื่อเจอสถานะที่ไม่คาดคิด แทนที่จะฆ่าทั้ง process หรือเดินหน้าต่อไปพร้อมยอมรับความเสียหาย มันจะย้อนกลับไปยังสถานะปกติที่รู้จักในระดับที่ละเอียดที่สุดเท่าที่ทำได้
    ไอเดียนี้เคยถูกวิจัยมานานแล้วในชื่อ “microreboots” และยังเกี่ยวข้องกับ “crash-only software” ด้วย แต่มีเพียง Erlang/BEAM ที่ทำให้สิ่งนี้เป็นแนวคิดชั้นหนึ่งของระบบปฏิบัติการ

    • ยังต้องระวัง supervision tree และการ restart บางส่วนของ tree อยู่ ตัวอย่างเช่น ถ้า process ของระบบปฏิบัติการ Erlang ทั้งตัวตายกะทันหันแล้วเริ่มใหม่ ระบบอาจทำงานได้ถูกต้อง แต่ถ้า restart เพียงบางส่วนของ process tree ใน Erlang ข้อมูลก็อาจเริ่มเสียหายได้
      Erlang ให้โมเดลที่ดีในการจัดการปัญหาแบบนี้ แต่ไม่ได้หมายความว่าจะปิดสมองไม่ต้องคิดได้ ถ้าเข้ามาด้วยความคิดว่าแค่ปล่อยให้มัน restart แล้วทุกอย่างจะโอเค ก็อาจเจ็บตัวได้
    • สงสัยว่าคำอธิบายนั้นต่างจาก exception handling อย่างไร
  • Erlang, OTP, BEAM ให้มากกว่า behaviour ตัว VM เองใกล้เคียงกับ virtual kernel ที่มี supervisor, process ที่แยกจากกัน และ distributed mode ที่จัดการเครื่อง physical/virtual หลายเครื่องเหมือนเป็น resource pool เดียว
    OTP ยังมี mode ที่มีประโยชน์หลายอย่าง เช่น Mnesia ซึ่งเป็น database, atomic counter สำหรับ caching และ ETS table เป็นต้น runtime ยังรองรับ hot reloading ของ bytecode ทำให้ apply patch ได้โดยไม่ต้องหยุดระบบ syntax ไม่ได้เป็นมิตรกับ screen reader นัก แต่ก็อ่านได้
    ถ้านึกถึงแพลตฟอร์มที่คล้ายกับ BEAM ในแง่ความสามารถในการจัดการ resource ของหลายเครื่องเหมือนเป็น pool เดียว ก็คงมีแค่ Apache Mesos[1] ประมาณนั้น
    เมื่อราวหนึ่งปีก่อน บริษัท consulting ส่วนตัวของผมนำ Erlang มาใช้เป็นภาษา backend หลังจากนั้นก็เริ่มสำรวจภายในของ BEAM เช่น เปลี่ยน stack ที่ใช้ TCP ไปเป็น QUIC หรือ integrate patch ของ Rust
    สำหรับระบบ lightweight และ throughput สูงที่ไม่ล้มเหลวเว้นแต่จะเกิด kernel panic หรือไฟดับ มันเป็นตัวเลือกที่ยอดเยี่ยมจริง ๆ ตอนนี้เรากำลังสร้างซอฟต์แวร์ที่ยุ่งมากและมี concurrency สูง เช่น tracker และ pipeline manager สำหรับการผลิตภาพยนตร์/เกม และกำลังเตรียม R&D สำหรับบริการจัดการโรงพยาบาลเอกชนด้วย
    [1]: https://mesos.apache.org/

    • ผมไม่มีแผนจะรับ Elixir หรือ Gleam มาใช้อย่างเต็มตัว เพราะ ecosystem ของฝั่งนั้นถูกสร้างโดยมี Phoenix framework และบริการ/ฐานข้อมูลภายนอกเป็นศูนย์กลาง
      มีความเป็นไปได้สูงว่าจะต้องดูแล binding หรือ implementation ภายในเองสำหรับสิ่งที่ฝั่ง Elixir ไม่ได้ maintain แล้ว
      อีกอย่างคือมี syntax sugar เยอะ และผู้ใช้ก็ดูมีความหมกมุ่นแปลก ๆ กับการ abstract อะไรต่อมิอะไรให้เป็น interface แบบ DSL
  • คำถามที่ว่า “ทำไมนักออกแบบภาษาและไลบรารีถึงลอกแค่แนวคิดเรื่องโปรเซสเบาและการส่งข้อความ แต่ไม่ขโมยโครงสร้างที่อยู่เบื้องหลัง Erlang behaviour ไปด้วย?” นั้นมีเหตุผลอยู่
    ซิกเนเจอร์ของฟังก์ชันใน Erlang behaviour ผูกแน่นกับฟีเจอร์อื่น ๆ ของ Erlang โดยเฉพาะ วิธีใช้ immutability ที่แปลกเฉพาะตัว นั่นเป็นเหตุผลที่เซิร์ฟเวอร์ต้องมีการเรียก init แยกต่างหาก และถ้าต้องการให้การจัดการสถานะทำงานในลักษณะเดียวกัน ก็จำเป็นต้องมีรูปแบบที่ชัดเจนมาก
    แต่ในภาษาอื่น หากจะบรรลุเป้าหมายเดียวกัน แทบจะมีหลายกรณีเสมอที่ไม่ควรคัดลอกสิ่งที่ Erlang ทำมาตรง ๆ ถ้ามีคนบอกว่า “พอร์ต gen_server ไปยังภาษาอื่นแล้ว” แต่แสดงอินเทอร์เฟซที่เหมือน Erlang ทุกประการ ผมมองว่าเป็นการพอร์ตที่ไม่ได้เข้าใจลึกซึ้งว่า Erlang กำลังทำอะไรอยู่
    ตอนที่ผมนำแนวคิด supervisor tree ไปใช้ใน Go[1] ก็ทำในแบบที่เป็น Go ใน Go ยุคใหม่ อินเทอร์เฟซที่ถูกต้องของ “สิ่งที่ถูกกำกับดูแลได้” ไม่ใช่ซิกเนเจอร์แบบเดียวกับ Erlang แต่มีแค่นี้
    type Service interface {
    Serve(context.Context)
    }
    ใน Go มีเท่านี้ก็พอ และควรใช้เพียงเท่านี้ ในภาษาอื่นอาจต่างออกไป ใน Go มี channel อยู่แล้ว จึงไม่จำเป็นต้องมี “handle_event/2” และควรใช้ channel ไม่ใช่เพราะ channel ดีกว่าหรือแย่กว่า แต่เพราะภาษานั้นทำงานแบบนั้น
    ในภาษาอื่นอาจใช้อย่างอื่นได้ และในโครงสร้างพื้นฐานอื่น ๆ แทนที่จะ “เรียก handle_event/2” ก็อาจส่งไปยัง Kafka หรือ cloud event bus ได้ ประเด็นสำคัญคือการสร้างระบบแบบ event-driven ไม่ใช่การคัดลอก implementation ที่ตรงเป๊ะของ Erlang
    ในชุมชน Erlang มีปัญหาเรื่องความมั่นใจเกินไปว่า วิธีที่ Erlang ทำอย่างแม่นยำนั้นมีอะไรพิเศษมหาศาล และถ้าทำต่างออกไปก็แปลว่าผิดและไม่น่าเชื่อถือทันที ในปี 2005 อาจเป็นเช่นนั้น แต่ในปี 2025 ไม่ใช่แล้ว
    ครั้งหนึ่ง Erlang แทบจะเป็นคำตอบเดียวที่สมเหตุสมผล แต่ปัญหาในปี 2025 คือการฝ่าทะลักของคำตอบจำนวนมหาศาล ผมแนะนำอย่างยิ่งให้เรียนรู้จาก Erlang เพื่อสร้างซอฟต์แวร์ที่เชื่อถือได้ แต่คัดค้านอย่างหนักต่อการพอร์ตวิธีที่ Erlang ใช้บรรลุผลแบบตรงตัวไปยังภาษาอื่นอย่างมืดบอด ในบริบทของภาษาอื่นแทบทั้งหมด นั่นเป็นคำตอบที่ผิด แม้แต่ภาษา immutable อื่น ๆ โครงสร้างก็ยังแตกต่างมากพอจนคัดลอกไปตรง ๆ ไม่ได้
    [1]: https://jerf.org/iri/post/2930/

    • ต่อจากนั้น คำถามถัดไปที่เป็นธรรมชาติก็คือ “ทำไมภาษาและระบบอื่น ๆ ที่มีแนวทางและแนวคิดแบบนี้จึงประสบความสำเร็จในการถูกตลาดนำไปใช้ แต่ Erlang/Elixir กลับไม่เป็นเช่นนั้น?”
      ในฐานะคนที่ใช้ Elixir เป็นอาชีพ นี่คือคำถามที่น่าสนใจที่สุดเกี่ยวกับ Erlang สำหรับผม
      เมื่อดูความสำเร็จอย่างมากของ k8s, Kafka และผลิตภัณฑ์ระบบกระจายของ AWS ก็ชัดเจนว่าความต้องการเครื่องมือที่ช่วยออกแบบระบบ concurrency ที่เชื่อถือได้นั้นมีมหาศาล แล้วทำไม Erlang/Elixir ถึงคว้าส่วนแบ่งนั้นไว้ไม่ได้?
      ผมคุยเรื่องนี้กับเพื่อน ๆ บ่อย ๆ แต่ก็ไม่รู้คำตอบ
    • ผมชอบ Go มากที่สุด แต่ยังสงสัยกับคำพูดที่ว่า “Go มี channel อยู่แล้ว จึงไม่จำเป็นต้องมี handle_event/2” channel ชนิดไหนกัน? ที่สำคัญกว่านั้น channel เป็นของ local process ดังนั้นถ้าจะทำให้เป็นเครือข่ายก็ต้องมีโค้ดกาวเชื่อม
      ผมคิดว่า Erlang น่าจะ abstract การจัดการข้อความบนเครือข่ายไว้แล้ว ยิ่งไปกว่านั้น ผมเคยเห็นแพตเทิร์นที่เสนอสำหรับสิ่งอย่าง long-running server แตกแขนงไป 3–4 แบบ
      ผมเห็นด้วยอย่างยิ่งว่าตอนพอร์ตควรใช้ส่วนประกอบที่เป็น idiomatic ของภาษานั้น ๆ แต่ภาษาหนึ่งอาจมีกลไกซ่อนอยู่ที่ทำให้สูญเสียแก่นสารอันมีค่าในกระบวนการพอร์ตได้ เรียกได้ว่าเป็นแนวคิดต่อต้านสัมพัทธนิยมของภาษาโปรแกรมมิงชนิดหนึ่ง
      “channel เหรอ? ก็แค่ห่อด้วย X ก็ได้แล้ว” อาจเป็นอันตรายต่อ interoperability มากกว่าที่ฟังดู ตัวอย่างเช่น http.Handler ของ Go นั้นเรียบง่าย แต่ข้อเท็จจริงที่ว่ามันอยู่ใน standard library มีนัยเชิงปฏิบัติที่ใหญ่ ทำให้เกิด ecosystem ของ middleware ที่โดยรวมแล้วเข้ากันได้โดยไม่ต้องตกลงกันล่วงหน้า
      io.Reader และพวกพ้องก็คล้ายกัน อินเทอร์เฟซที่เรียบง่ายสุดขีดเช่นนี้อาจมีคุณค่ามากกว่า implementation ด้วยซ้ำ
      เป็นการคาดเดา แต่ถ้า Erlang จับอินเทอร์เฟซจำนวนมากสำหรับระบบกระจายที่เชื่อถือได้ได้ถูกต้อง นั่นอาจเป็นปัจจัยที่ทำให้ทั้งหมดเป็นไปได้
  • ไม่เห็นด้วย อินเทอร์เฟซเป็นแนวคิดเล็กน้อยที่ใส่เข้ากับภาษาไหนก็ได้ แม้ในภาษาที่ไม่มีไวยากรณ์ interface อย่างเป็นทางการ ก็สามารถเลียนแบบในพื้นที่ของโปรแกรมได้
    เหตุผลที่ BEAM ประสบความสำเร็จคือสามารถรันโปรเซส 1 ล้านตัวบนโหนดเดียวได้ แสดง state machine แบบกระจายที่ซับซ้อนได้ง่าย และรีสตาร์ตบางส่วนของระบบได้โดยไม่หยุดให้บริการ ยังมีเหตุผลอื่นอีกมาก
    ผมไม่คิดจริง ๆ ว่า behaviour/interface เป็นชิ้นส่วนที่เป็นแก่นสำคัญที่สุด

    • เห็นด้วยอยู่บ้าง ตอนเห็น Erlang ครั้งแรก มันดูเหมือนว่าทุกฟีเจอร์ที่ Erlang มี ระบบอื่น ๆ ก็มีสิ่งคล้ายกันอยู่
      heap ของโปรเซสที่แยกจากกันก็ใช้ OS process ได้, supervisor tree ก็ใช้ Kubernetes ได้, การส่งข้อความก็ทำได้ใน Java ด้วย thread สองตัวกับ shared queue, hot code loading Java ก็ทำได้ และงาน latency ต่ำก็อาจจูน LMAX Disruptor จนชนะ Erlang ได้ เป็นต้น
      แต่หัวใจสำคัญคือการรวมทั้งหมดนี้ไว้ในแพลตฟอร์มหรือไลบรารีเดียว OS process หนัก และไม่ง่ายที่จะรัน 2 ล้านตัวบนเซิร์ฟเวอร์ ถ้าใช้ green thread หรือ promise ก็จะเสีย heap ที่แยกกันไป
      Kubernetes ก็ใช้ได้ในระดับหนึ่ง แต่ไม่ได้จัดการ nested supervisor tree ได้ดีนัก อาจทำได้ก็จริง แต่จะมีสิ่งสารพัดนอกจากโค้ดเพิ่มขึ้นมา ทั้ง pod, controller, volume ฯลฯ
      การส่งข้อความทำได้ด้วย actor library ของหลายภาษา แต่ไม่ได้ผสานความสามารถอย่างโปร่งใสในการ pattern matching ใน receive หรือส่งไปยัง thread อื่นบน node อื่น
      hot code loading ก็ทำได้ แต่ปัญหาคือจะจัดการ runtime data structure และ state อย่างไร Erlang ถูกสร้างโดยมีเรื่องนี้เป็นศูนย์กลาง และเพราะ state ของ gen_server เป็น immutable และ explicit จึงมี callback สำหรับอัปเกรดไม่เพียงโค้ด แต่รวมถึงตัว state เองด้วย
    • ยังไม่ได้ใช้มากพอ แต่ผมคิดว่า BEAM หลีกเลี่ยงบางส่วนของปัญหาที่ Ian Cooper ค้นพบใหม่ใน “Where Did It All Go Wrong?” นั่นคือปัญหาที่ microservices ไม่ได้ optimize แรงเสียดทานภายในโมดูล/ระหว่างโมดูลของระบบ
      ผมไม่ได้บอกว่า BEAM กำจัดปัญหานี้ได้ แต่ดูเหมือนจะลดความชันของเส้นลง idiom และฟีเจอร์ที่สอดคล้องในตัวเอง รวมถึงการ deploy, self-healing และ load balancing ช่วยลดแรงเสียดทานระหว่างโมดูล
      มันทำให้วิศวกร 12 คนสามารถจัดการ endpoint 30 ตัวได้อย่างง่ายดาย และสร้างระบบที่พื้นที่ผิวก็ยังคงเป็นไปตาม power law ได้
  • ไม่เห็นด้วยกับเนื้อหาในบทความนี้ Behaviour เป็นไปได้เพราะสถาปัตยกรรมพื้นฐานของระบบ
    Behaviour ไม่ใช่อินเทอร์เฟซ แต่ใกล้เคียงกับ อ็อบเจ็กต์นามธรรม ในภาษาอย่าง Java มากกว่า มันทำหน้าที่พื้นฐานที่สมบูรณ์ในตัวเองซึ่งซ่อนอยู่หลังอินเทอร์เฟซสำหรับการทำงานร่วมกัน แต่หากไม่มีโครงสร้างพื้นฐานรองรับที่รับประกันว่าแต่ละโปรเซสจะแยกขาดจากโปรเซสอื่นโดยสิ้นเชิง, สามารถปิดโปรเซสทั้งหมดได้อย่างปลอดภัยโดยไม่ทำให้หน่วยความจำหรือทรัพยากรรั่วไหล, และไม่สามารถแชร์พอยน์เตอร์อันตรายระหว่างสองโปรเซสได้ ก็ทำอะไรได้ไม่มาก
    สิ่งที่ Joe แสดงให้เห็นในวิทยานิพนธ์คือ จะสร้างระบบที่เชื่อถือได้ และยิ่งไปกว่านั้นคือระบบกระจายที่เชื่อถือได้ในระดับหนึ่ง จากชุดบล็อกเลโก้ที่มีอยู่ได้อย่างไร
    หากจะทำสิ่งนั้นให้ถูกต้องจริง ๆ จำเป็นต้องมี Erlang VM และไม่สามารถทำให้สมบูรณ์ได้บน VM อื่น หากไม่มีงานระบบท่อระดับล่าง supervision tree ก็จะรั่วได้ ใน Java คุณไม่สามารถฆ่าเธรดที่ถือครองทรัพยากรอยู่แล้วคาดหวังว่าทุกอย่างจะเป็นไปด้วยดีเสมอได้ และยังขาดวิธีเฝ้าติดตามโปรเซสต่าง ๆ ด้วย