ทำ Makefile ให้ใช้เฉพาะ GNU

ใช้ชื่อไฟล์ GNUmakefile แทน Makefile และหัวไฟล์ใส่ SHELL := /bin/bash

แบบนี้ BSD Make จะไม่เรียกใช้ ก็จะไม่คงว่าทำไมใช้ไม่ได้ บน Debian ถึงจะใช้ GNU Make แต่ใช้ shell ด้านในเป็น Dash ก็มักจะพังอยู่ดี ก็เลยระบุไปเลยว่าใช้ Bash

หรือจะเปลี่ยนไปเขียน Makefile ให้ใช้กับ BSD Make และ /bin/sh ก็ได้ แต่ผมทำไม่ไหว

Advertisement

เดาเรื่อง Blockchain ของเพื่อไทย

  1. เพราะว่าเป็นแนว micro payment ผมจึงคิดว่าใช้ Layer 2 พวก off-chain ทั้งหลาย เช่น Lightning network
  2. ข้อจำกัดเรื่อง 4 กิโลเมตร น่าจะจำกัดการเปิด channel แทนที่จะเป็น smart contract บน L1
  3. โครงการทำได้สะดวก เพราะมี open source software อยู่แล้วทั้ง wallet ทั้ง node ทั้ง server ที่เอาไว้เปิด channel

หูฟัง Beyerdynamic ที่ผมใช้

ถึงมันจะผิดประเภทหรืออะไรก็ตามก็ใช้ตามนี้

  • DT 231 Pro
  • XP 1
  • DTX 501p
  • DT 240 Pro

ถึงจะใช้รุ่น Pro อยู่สองตัวแต่ก็ไม่ได้ทำงานเรื่องเสียงเลยนะ บางทีก็แค่เอามาฟังเพลง บางทีก็ฟังพระเทศ ฯลฯ

ตัวแรกที่ใช้คือ DT 231 Pro ประทับใจเลยเสียงดี ทนทานสุด ๆ ตอนนี้ยังใช้ได้อยู่เลย แต่คงต้องซ่อมพวกฟองน้ำ ยางยืดแล้ว ส่วน XP 1 ตามมาเพราะเอามาฟังบนรถตู้ เป็น in ear เล็ก ๆ DTX 501p เห็นลดราคาเยอะก็เลยจัดมา เป็นแบบ on ear ธรรมดา เบา ๆ พกพาสะดวก กล่องสวย ตัวล่าสุดคือ DT 240 Pro อันนี้ความสบายน้อยสุดแล้วบีบหัว แต่ก็ปิดเสียงรอบข้างดี มีหัวแปลง 3.5 มม.เป็น 6.5 มม. ด้วย

ใน 4 ตัวนี้ผมชอบเสียง DT 240 Pro ที่สุด ส่วนตัวที่ขอบน้อยที่สุดคือ XP 1 แต่ความสะดวกเวลาใช้งาน ก็พกพา XP 1 มันก็ดีกว่าตัวที่เหลือเยอะ พอนั่งรถตู้ยาว ๆ เพลงส่วนมากก็พังแต่ช่วงแรก ๆ พอนั่งไปนาน ๆ ก็กลายเป็นหูฟังที่ใช้ฟังเสียงอ่านพระสูตรซะงั้น DTX 501p พกพาง่ายแต่ว่าใจได้พังยากนิดนิง กว่าจะรื้อกล่องออกมาจากเป้ กว่าจะรูดซิบออกมาจากกล่อง แต่ก็พกง่ายกว่า DT 231 Pro และ DT 240 Pro มาก เสียงถ้าไม่ตั้งใจจับผิดมาก DTX 501p ก็คือดีเลย

List ใน Clojure ตอนที่ 1

ภาษาโปรแกรมยอดนิยมต่าง ๆ มักจะมีลิสท์ ( list ) แถมมาให้ด้วย เช่น Python หรือภาษาอื่น ๆ ก็มีเหมือนกันถึงแม้ว่าอาจจะเรียกอย่างอื่นก็ตาม อย่างไรก็ตามลิสท์ในภาษา Clojure และกลุ่มภาษา Lisp พิเศษที่ว่าโปรแกรมก็เป็นลิสท์ด้วย ทำให้เราเขียนโปรแกรมขึ้มมาเปลี่ยนแปลงอีกโปรแกรมหนึ่งด้วยวิธีเดียวกับที่จัดการลิสท์ ทำให้กลุ่มภาษา Lisp ยืดหยุ่นพร้อมสำหรับโปรแกรมรูปแบบใหม่โดยที่ไม่ต้องไปแก้ compiler/intepreter

ใน Python หรืออีกหลาย ๆ ภาษาลิสท์หน้าตาก็จะออกมาแบบข้างล่างที่ใช้ [ ] ครอบ

[10, 20, 30]

ส่วนลิสท์ใน Clojure เขียนเป็น

(10, 20, 30)

ซึ่งก็คล้าย ๆ Python เหมือนกันเพียงแต่เปลี่ยนเป็น [ ] และ ( ) แต่ที่มากไปว่านั้นคือ ไม่ต้องใส่ , ก็ได้ และส่วนมากก็ไม่นิยมใส่กันก็จะเขียนได้แบบด้านล่าง

(10 20 30)

แต่ว่าโปรแกรมใน Clojure ก็เป็นลิสต์ด้วย เช่น

(print 10 20)

อันนี้คือเรียกฟังก์ชันชื่อ print โดยส่งค่า 10 และ 20 เข้าไปด้วยตอนเรียก พอไปเจอแบบ

(10 20 30)

Clojure ก็จะมองว่าเรียกฟังก์ชันชื่อ 10 และส่งค่า 20 และ 30 ไปด้วย ซึ่งควรจะพังเพราะว่ายังไม่มีฟังก์ชันชื่อ 10 ดังนั้นถ้าจะเอาลิสต์นี้ไปใช้อย่างอื่นที่ยังไม่ใช่การเรียกฟังก์ชันเดี๋ยวนี้เราก็บอก Clojure ได้โดยที่ใส่ ‘ เข้าไปข้างหน้า ซึ่งเรียกว่า quote ซึ่งจะไปบอก Clojure ว่าปล่อยลิสต์ไว้แบบนั้นไม่ต้องไปประมวลผลอะไร ก็จะได้ออกมาหน้าตาแบบนี้

'(10 20 30)

ตอนนี้ที่ 1 นี้ผมจะจบไว้เท่านี้ก่อน ตอนต่อไปยังมีเรื่องอีกมาก เช่น จะจัดการลิสท์อย่างไร ยังมี data structure อื่นอีกไหม จะเอาเขียนโปรแกรมที่เปลี่ยนแปลงโปรแกรมอย่างไร

เกี่ยวกับ type ใน Lisp

Lisp กลายเป็นกึ่ง ๆ static typing อย่างช้าที่สุดตั้งแต่ค.ศ. 1977

จุดประสงค์คือต้องการทำให้ประมวลผลเร็วขึ้น ซึ่งต่างจาก TypeScript Python Elixir ที่เอา type ไม่ได้ช่วยตอนสร้างภาษาเครื่องเลย

ลักษณะแบบนี้มันติดมาใน InterLISP, Common Lisp และ Clojure ด้วย

สมัยนี้นอกจากเอาข้อมูล type มาเร่งความเร็วแล้ว Common Lisp และ Clojure (ผ่าน clj-kondo) ก็เอามาช่วยจับผิดโปรแกรมเมอร์ได้

ถ้าไม่ชอบเอาวงเล็บไว้หน้าสุด Julia ก็ได้

โครงการแปลภาษาผมเห็นปัจจัยที่ควรพิจารณา 4 อย่าง ที่ไม่ใช่เรื่องใหม่ เคยเห็นตำราประมาณการแปล 101

1. การสื่อความหมายที่ตรงกับภาษาต้นทาง
2. ระดับการใช้ภาษาที่เหมาะสมกับกลุ่มเป้าหมาย
3. ความเป็นธรรมชาติ
4. ความคงเส้นคงวา

ข้อ 2 ผมว่ายาก เพราะหวังให้คนที่ไม่รู้ภาษาอังกฤษไม่รู้บาลีใช้ แต่ไม่รู้ว่าเขาไม่รู้ขนาดไหน ก็เลยมีกลุ่มเพื่อนนักแปลขึ้นมา https://www.facebook.com/groups/662788240558788 หรือบางนี้ผมก็ไปถามรปภ.หรือจนท.ธุรการดู

เท่าที่แปล mozilla.org มา เน้นข้อ 1 กับข้อ 4 ก่อน แล้วค่อยมาดูบนเว็บจริง ๆ แล้วแก้ข้อ 2-3 อีกที แต่ก็ไม่ได้แน่นอนขนาดนั้น ถ้าอยากทำข้อ 2-3 ไปด้วยเลยก็ได้ แต่พลังคนตอนนี้ถ้าทำข้อ 2-3 ไปด้วยเสร็จไปมีทันแน่ ๆ

ฐานข้อมูลแบบกระจายอำนาจ

จะมองว่า MongoDB มัน decentralized หรือกระจายอำนาจก็ได้ หรือแม้แต่ MySQL dBase SQLite ในแง่นึงพวกนี้ก็กระจายอำนาจทั้งหมด

ผมยกตัวอย่างภาครัฐบางแห่ง ระบบของกระทรวงหรือกรมหรือกองต่าง ๆ ฐานข้อมูลก็มีการกระจายอำนาจได้ ซึ่งทั้งข้อมูลและ schema ไม่ได้ถูกกำหนดจากส่วนกลางก็มี

ส่วนที่เป็นงานงอกมีอยู่สองแบบครับ คือ

1. จะเปลี่ยนพวกนั้นมาเป็นระบบ centralize ได้ไง

2. ทำอย่างให้ decentralized database ทำงานด้วยกันได้

แบบที่่ 1 เราไม่คุยแล้วกัน

แบบที่ 2 มีทั้ง

2.1 Web API ซึ่งแต่ละที่ก็ไม่ได้มีมาตรฐานอะไรอยู่ดี คนที่นำมาใช้ก็แปลงข้อมูลกันเอาเอง

2.2 dump database ออกมาเลย แล้วใช้ ftp หรืออะไรก็ตามส่งข้อมูลเอา แล้วใครใช้ก็แปลงกันเอาเอง

2.3 แต่ละองค์แปลงมาเป็นรูปแบบกลาง แล้วค่อยส่งต่อหรือมีอะไรมาดึงออกไป

ข้อ 2.3 นี่คนทำเครื่องมีมาขายมาแจกเยอะเลยนะครับ แต่ผมข้ามไปแล้วกันเพราะจำชื่อไม่ได้

ข้อมูลกลางในตระกูล data ของประเทศต่าง ๆ คือจะเก็บอยู่ในรูปแบบ RDF และใช้ vocabulary กลาง หรือถ้าคิดแบบ RDBM ก็มองว่าเป็นชื่อ column ก็ได้ แต่ถ้าไม่มี vocabulary กลางใช้ก็ตั้งขึ้นมาเองก็ได้

เสร็จแล้วก็เอาข้อมูลไปใส่ triplestore ก็มี service ให้ query ด้วย SPARQL

ซึ่ง SPARQL มี federated mode คือใน query เดียวสามารถ query จาก service หลายตัวได้

เรื่อง decentralized database ก็จบลงด้วยประการละฉะนี้

Rust กับ GCC

compiler เดี๋ยวนี้มี front-end แปล source code ภาษาต่าง ๆ ไปเป็นตัวแทนตรงกลาง เช่น frontend ตัวนึงแปลภาษา Rust ไปเป็น LLVM IR หรือ frontend อีกตัวนึงแปลภาษา C ไปเป็น LLVM IR แล้ว backend ก็เอา LLVM ไป optimize แล้วแปลเป็นภาษาเครื่องแบบต่าง ๆ เช่น AMD64 Aarch64 อีกที

การแบบแบบนี้มันลดงานลงคนที่อยากสร้างภาษาใหม่ก็ทำ frontend ใหม่ก็พอ หรือคนที่อยากรองรับ instruction set แบบใหม่เช่น RISC-V ก็ทำ backend ใหม่ภาษาที่มีอยู่แล้วก็ใช้งานได้เลย

นอกจาก LLVM แล้ว GCC ก็ลักษณะแยก front-end back-end เหมือนกัน แต่ก็มี compiler อีกหลายตัวที่ทำเองครบหมดเลย เช่น compiler เก่า CMUCL ที่มีมาก่อน GCC และ LLVM ก็มี compiler ที่เอาไว้ compile Common Lisp อย่างเดียว

ค่าย ๆ GCC ถ้าไม่ทำ frontend ใส่ GCC เพิ่ม ก็เพียงแต่เอา libgccjit มาใช้กับ compiler เก่าที่มีอยู่แล้ว ถึงจะชื่อ libgccjit ก็จริง ๆ แต่ก็ compile แบบ ahead-of-time (AOT) ได้เลย ตอนนี้ Emacs Lisp ก็ใช้แบบนี้อยู่

backend ของ GCC ยังได้เปรียบ LLVM ที่รองรับชุดคำสั่งบางแบบที่ LLVM ทำให้ไม่ได้ เขาว่างั้น แต่ผมก็ไม่แน่ใจว่ามีอะไรบ้าง แต่ก่อน LLVM ใช้กับ extensa ไม่ได้ แต่ตอนนี้ Express-IF ก็ทำให้แล้ว

Rust ก็มีโครงการทั้งสองแบบคือทำ Rust frontend สำหรับ GCC ชื่อ gccrs กับอีกโครงการคือ rust_codegen_gcc ใช้ rustc เหมือนเดิมแต่ว่าไปเรียก libgccjit เพื่อแปลเป็นภาษาเครื่องแทน

https://lwn.net/Articles/907405/

unit test กับ REPL

software test นี่ทำ bot มาลาก mouse กด keyboard ก็ได้ สร้าง API เทียมทั้งหมดเลยก็เป็นไปได้ แต่ผมนึกออกอยู่สองอย่างคือ

1. ทำแบบที่ว่ามันออกแรงเยอะเทียบกับ test พวก pure function มาก ๆ

2. test แล้วก็อาจจะยังบอกไม่ได้ว่าไปพังที่ไหน

ดังนั้นควรจะมี unit test ด้วย

ส่วน TDD ผมว่าช่วยได้มากเลย ไม่งั้นผมนึกไม่ค่อยออกว่าจะไปใส่ test ที่ไหนเมื่อไหร่ บางทีก็ลืมตัวไปทำโปรแกรมที่ test ยาก

ผมไม่ได้คิดว่า TDD เหมาะกับทุกอย่าง เช่น บางทีนึกภาพไม่ออกด้วยซ้ำว่าจะ test อะไร และเริ่มเอา Pandas โหลดข้อมูลมาดู

นอกจากท่าพวก Jupyter Notebook ร่วมกับ Pandas แล้ว ผมว่า shell ต่าง ๆ เช่น Bash และ Powershell มันก็เป็นการเขียนโปรแกรมแบบลองรันก่อนเลย ไม่ได้คิด test case ก่อน พอรันคำสั่งนึงได้แล้วค่อยไปเขียนอีกคำสั่งนึงมากต่อ ๆ กันแล้วมัดรวมกันเป็น shell script; Lisp และพวก ผมว่าก็อยู่ในข่ายนี้เหมือนกัน คือลองรันดูก่อนแล้วก็โปรแกรมมาต่อ ๆ กัน

กลุ่มนี้ที่มี Jupyter Notebook, shell, Lisp ผมจะเรียกรวมกันเลยว่ากลุ่ม interactive

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

การสร้างคำสั่งใหม่ถึงแม้ว่าจะไม่ได้พัฒนาโปรแกรมแบบโต้ตอบก็มีการสร้างคำสั่งใหม่อยู่ดี ยกเว้นจะเขียน assembly แบบไม่มี macro หรือภาษา BASIC รุ่นใช้เลขบรรทัด

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

เมื่อผลลัพธ์การรันแต่ละคำสั่งย่อย ๆ มันเยอะเกินไปกว่าที่คนจะคอยมาตรวจเอง หรือว่าต้องรันหลายครั้งหลายแบบเกินไปจนเป็นตัวถ่วง ก็ค่อยสร้าง test อัตโนมัติก็ได้ ผมพอจะเห็นเครื่องมือในลักษณะนี้ เช่น testbook[1] ก็ใช้ test ฟังก์ชันที่อยู่ใน Jupyter Notebook และ bash_unit ที่เอาไว้ test shell script ส่วนของ Lisp ก็มี Fiveam

จะว่ากันไปจริง ๆ เครื่องมือสำหรับ unit test ก็เกิดมาจากกลุ่มโต้ตแบ เพราะ unit test ตัวแรกที่สาธารณะรู้จักคือ sUnit ก็มาจากหนังสือ Simple Smalltalk Testing: With Patterns ของ Kent Beck เมื่อ 33 ปีที่แล้ว ซึ่ง Smalltalk ก็เป็นที่รู้จักกันดีเลยว่าไม่ใช่แค่ภาษาแต่เป็นสิ่งแวดล้อมในการพัฒนาโปรแกรมแบบโต้ตอบในโหมดกราฟฟิค

กลุ่มโต้ตอบมันไม่เห็นภาพเลย จะลองเรียกว่าการพัฒนาแบบรันดูผลทีละคำสั่งหรือละหลายคำสั่งแล้วกัน พอเรียกแบบนี้ชวนให้คิดเลยว่าต้องใช้ interpreter เท่านั้นใช้ compiler ได้ ซึ่งไม่จริง เพราะมีตัวอย่างตามนี้ ตัวอย่างแรก Jupyter Notebook ใช้ Swift ได้ซึ่งใคร ๆ ก็รู้ว่า Swift ใช้ compiler และอีกอันคือ Interlisp อย่างน้อย 49 ปีหรือนานกว่านั้นใช้ compiler เหมือนกัน

ประเด็นหนึ่งเถียงกันตามเครือข่ายสังคมออนไลน์คือเรื่อง static type พ่อทุกสถาบัน หรือว่า dynamic type สิของจริง

เป็นเรื่องจริงว่าการพัฒนาโปรแกรมแบบรันดูผลทีละส่วน ภาษาแบบ dynamic type เป็นที่นิยมกว่าแบบ static type ผมจิตนาการว่าได้ไฟล์ข้อมูล csv มาหนึ่งไฟล์ เราไม่ได้มีความรู้มาก่อนว่าไฟล์นั้นมันต้อง type อะไรบ้าง หรือว่าแต่ละบรรทัดซึ่งอาจจะมีเป็นล้านบรรทัดจะมีข้อมูลอะไรขาดไปได้บ้าง ก็เลยง่ายกว่าที่จะโหลดข้อมูลขึ้นมาประมวลผลก่อน ซึ่งหลายครั้งก็ใช้แค่คอลัมน์เดียวก็มี

ลักษณะที่ว่าไม่รู้จักข้อมูลโดยละเอียดมาก่อน มันเข้าทาง dynamic type พอดี

แต่ว่าก็ไม่จำเป็นต้องเป็น dynamic type เท่านั้น บางทีเราอาจจะมีฟังก์ชันเสริม เช่น ตัดคำ ตัดเครื่องหมายพิเศษออก คำนวณกำไร พวกนี้เรารู้อยู่แล้วว่าตัดคำตัดเครื่องหมายพิเศษพวกนี้ต้องใช้ string แน่ ๆ คำนวณกำไรพวกนี้เป็นตัวเลขแน่ ๆ ไม่จำเป็นต้องมา dynamic พอไม่ dynamic ก็ช่วยให้ compiler หรือ linter ช่วยหาข้อพิดพลาดได้เยอะขึ้นละเอียดขึ้น ในบางระบบเช่น Common Lisp โปรแกรมก็อาจรันไวขึ้นด้วยถ้าระบุ type ก่อน compile

อย่างแรกคือภาษา static typing เอามาใช้ใน Jupyter Notebook ก็มี เช่น Swift หรือ Rust

หรือกระทั่งเดี๋ยวนี้ พ.ศ. 2565 ภาษา dynamic typing ก็ใส่ type ได้ check type ได้ หรือภาษา static typing ก็มี type ที่แทบจะใส่อะไรก็ได้คล้าย ๆ กับ dynamic type

45 ปีก่อน MacLisp ก็ใส่ type แล้วก็ compile ทำให้ความเร็วออกมาดี ทั้งที่ Lisp เป็นภาษาแบบ dynamic typing ทุกวันนี้ Common Lisp compiler ก็ check type ได้ Julia ก็คือมาแนวเดียวกันเลย ส่วน Python Ruby เดี๋ยวนี้ก็ใส่ type ได้แล้วมี linter แยกมา check อีกที

สรุปว่าควรจะ test นั่นล่ะ แต่ถ้า TDD แล้วรู้สึกว่าไม่ไหว ก็มาแนวรันแล้วดูผลทีละฟังก์ชันก็ได้ วิธีนี้ก็ไม่ได้มีข้อจำกัดด้วยว่าต้อง compile หรือ interpret จะใช้ dynamic type หรือ static type ก็ได้

ไม้เรื่อง Helix แต่ไม่เคยเขียน

ClojureScript อยากใช้ React Hooks ก็ได้ อาจจะมาแบบไม่ใช้ framework มาครอบเลย หรือว่าจะใช้ framework ก็มี คือ Helix [1]

ดูจากตัวอย่าง[2] แล้วส่วน view ก็อาจจะเฉย ๆ ถ้าไม่ได้ชอบ Lisp ส่วนที่รู้สึกว่าน่าจะง่ายกว่าเขียน JavaScript ตรง ๆ คือ use-persisted-reducer อันนี้ก็เหมือนได้ immutable data มาเลย และใช้ operation ที่คุ้นเคยอยู่แล้ว

[1] https://github.com/lilactown/helix
[2] https://github.com/lilactown/helix-todo-mvc/blob/master/src/todo_mvc/core.cljs#L42