มอง life time ของ Rust เป็น partially ordered set

ใน blog นี้ https://ticki.github.io/blog/lambda_crabs_1/ ที่ผมอ่านนานมาก เพราะอ่านไป 2-3 บรรทัดแล้วหลับ

life time เรียกอีกอย่างว่า region ก็ได้ เป็นเวลาหรือพื้นที่ใน code ที่ตัวแปรหนึ่งใช้งานได้ ใน blog นี้เรียกว่าอธิบายสัก 90% แทบจะไม่มี code เลย ถ้าชอบแนวอธิบาย blog นี้มันดูเหมาะมาก ๆ

life time ของ Rust นี่เราคาดเดาได้เลยว่ามันจะจบเมื่อไหร่ แล้วมันก็มักจะตัดสินตั้งแต่ตอน compile เลย ไม่เหมือน Lisp ที่ garbage collector มันไปทำงานเวลารัน แต่ปัญหา programmer ไม่รู้ว่า compiler มันตัดสิน life time อย่างไร ใน blog นี้เขาก็เลยใช้คณิตศาสตร์มาช่วยอธิบาย

อันนี้เขาก็บอกว่า region นี่มองมันว่าเป็น partially order set หรือเรียกสั้น ๆ ว่า poset นะ ซึ่งเห็น poset แล้วก็น่าจะเดาคร่าว ๆ ได้ว่าเดี๋ยวตอนต่อไปจะมี semiring และ lattice ต่อแหง ๆ

อีกประเด็นก็คือ life time นี้เวลา implement มันก็จะเป็น type นี่เอง แล้ว Rust compiler จะไป check life time ผ่านเหมือนการ check sub type

อ่านแล้วก็รู้สึกว่าเจ๋งดีแต่ไม่รู้ว่ามีประโยชน์อะไรหรือเปล่า คงต้องอ่านตอน 2 และตอน 3 ต่อไปก่อน แต่ก็ยังอ่านไม่เจบอีกตามเคย

intorust.com

ผมฟัง intorust.com ที่ได้ link มากจาก Rust ไทย ข้ามไป 3 บท บทที่ 5 คือ Shared borrow ฟังแล้วรู้สึกประทับใจ นอกจากจะพูดเรื่อง borrow ชัดเจนดีแล้ว ยังมีเรื่อง string slice ด้วย เป็นสิ่งที่เคยงง ๆ เหมือนกัน แล้วก็เพิ่งทราบด้วยว่า .split(…) ของ Rust ไม่ต้องสร้างกระทั่ง string ใหม่เลย แบบนี้ก็น่าจะเลี่ยง malloc ไปได้เยอะเลย

rust_tut1

ท่าดัก error ในภาษา Go กับ C

เวลาเขียน Go แล้วเจอ

if err != nil {

}

นี้เราอาจจะคิดว่ามันน่าเบื่อ มันอาจจะคล้าย ๆ กับ try: except: เวลาเขียน Python แต่ว่า ปกติแล้ว ถ้ามี exception เกิดขึ้น Python มันจะหยุดทำงานใน flow ปกติไปเลย ยกเว้นเราจะระบุไว้ไปเป็นอย่างอื่น แต่ Go นี่ถ้าไม่ระบุอะไร เช่นบอกว่าให้ return หรือ panic มันก็จะทำงานของมันต่อไปเรื่อย ๆ

แต่พอมาดูกรณีภาษา C ที่พี่ bact ยกมาเมื่อวาน ซัดกับ iconv จาก issue นี้

iconv มัน return size_t ออกมาซึ่งจริงๆ แล้ว เป็น unsigned integer แต่ว่าถ้ามี error มันจะ return -1 ที่ถูก cast เป็น size_t แล้วออกมา เวลาจะตรวจสอบว่ามี error หรือเปล่าต้องทำแบบนี้

if (res != (size_t) -1) {
   
}

มองในแง่นี้ Go ก็ดูดีขึ้นมาทันที อาจจะเขียนเยอะไปบ้าง แต่ก็ทำอะไรชัดเจนตรงไปตรงมาดี ถึงแม้บางทีผมจะรู้สึกว่าน่าเบื่อก็ตาม

โปรแกรมตัดคำภาษาไทยอย่างง่าย สำหรับใช้กับ Python 3

#! -*- coding: UTF8 -*-
from wordcut import Wordcut
if __name__ == '__main__':
    with open('bigthai.txt') as dict_file:
        word_list = [w.rstrip() for w in dict_file.readlines()]
        word_list.sort()
        wordcut = Wordcut(word_list)
        print(wordcut.tokenize("กากา cat หมา"))

ใช้แบบข้างบนเลยครับ โปรแกรมโหลดได้จาก https://gitlab.com/veer66/wordcutpy บน github ก็มีครับชื่อเดียวกันถ้าทนใช้ gitlab ไม่ไหว

ตัวนี้พยายามจะให้มันง่ายนะครับไฟล์เดียวเสร็จเลย ยังไม่ได้ test เยอะ ถ้าเจอปัญหาอะไรแจ้งใน gitlab ได้เลยครับ ถ้ามี testcase ให้เลยจะดีมาก ถ้ามี testcase ด้วย pull request มาด้วยเลยก็จะดีมาก ๆ ครับ🙂

ป.ล. ใช้ได้กับ Python 3 ขึ้นไปเท่านั้นครับ เพราะว่าพี่ bact ใช้ Python 3😛

lazy-seq #Clojure

เรื่องนี้ออกตัวก่อนว่าจริง ๆ ผมก็ยังงง ๆ อยู่ แต่เขียนบันทึกสิ่งที่คิด ๆ ไว้ก่อน

เมื่อวานผมอ่าน lazy sequence มาจากไฟล์แล้วเอามา process ต่อ loop-recur ใน loop recur ก็เอาผลลัพธ์มาต่อ ๆ กันด้วย cons รู้สึกแบบนี้มันไม่ค่อยมีประโยชน์เพราะว่า ผลลัพธ์ทั้งหมดก็จะอยู่ใน ram หมดเลย

ก็เลยคิดว่าน่าจะทำเป็น lazy sequence ได้ แต่พอไม่ใช่ filter map เรื่องมันก็เริ่มจะไม่ตรงไปตรงมาแล้ว ผมพยายามจะลดรูปปัญหาให้ง่าย ๆ อย่างนี้ มีไฟล์ csv ประมาณนี้โดยที่เรียง (sort) ตาม column แรกไว้

1,cat
1,man
1,can
3,ruby
3,prolog
4,perl

สิ่งที่ต้องการคือถ้ามี id ซ้ำกันเอาแค่ row แรกพอ เช่นจากด้านบนผลที่จะได้คือ [[1 cat] [3 ruby] [4 perl]] แต่ต้องจิตนาการว่าไฟล์ใหญ่สัก 20GB อะไรแบบนี้นะครับโหลดขึ้น ram หมดไม่ได้

อ่านไปอ่านมาก็พอว่าต้องใช้ macro ชื่อ lazy-seq พอเขียน code ก็ออกมาได้อย่างนี้

(defn distinct-id [rows]
  (lazy-seq
   (when-let [s (seq rows)]
     (let [row (first s)]
       (cons row
             (distinct-id 
                (drop-while
                     #(= (first row) (first %))
                     (rest s))))))))

ไม่รู้ว่ามันใช้ได้จริงหรือเปล่าเลยลองยัด data ที่มันไม่สิ้นสุดใส่เข้าไป

(take 3 (distinct-id (map list (range) (range))))

ถ้าหากว่ามันไม่ lazy จะ stack overflow เลย เพราะมันหาไปเรื่อยไม่เสร็จสักที แต่ปรากฎว่าก็ใช้ได้

เพื่อความชัวร์ก็ลองเอา lazy-seq ออก

(defn distinct-id [rows]
  ;;(lazy-seq
   (when-let [s (seq rows)]
     (let [row (first s)]
       (cons row
             (distinct-id 
                 (drop-while 
                     #(= (first row) (first %))
                     (rest s)))))))

แล้วรัน (take 3 (distinct-id (map list (range) (range)))) อีกทีก็บึ้มตามคาด

when-let นี้ผมไม่รู้เอามาจากไหนก็เลยลองเอาออกดู แก้เป็นแบบนี้

(defn distinct-id [rows]
  (lazy-seq
   (when rows
     (let [row (first rows)]
       (cons row
             (distinct-id 
                 (drop-while 
                     #(= (first row) (first %))
                     (rest rows))))))))

 

ก็ทำงานได้ปกติ

ตอนนี้ตามที่เข้าใจคือ lazy-seq มันจะสร้าง object มาหุ้ม body คือ

 

 ;; body
 (when rows
     (let [row (first rows)]
       (cons row
             (distinct-id 
               (drop-while 
                   #(= (first row) (first %))
                   (rest rows))))))

 

ไว้

พอมีการเรียกใช้งาน เช่น (first (distinct-id (map list (range) (range)))) มันก็จะมาเรียกใช้ body 1 ที แล้ว body มันจะคืน sequence หรือจริง ๆ แล้วก็คือ Cons ออกไป โดยจะมี 2 ส่วนคือส่วน first ได้แก่ row ที่คำนวณเสร็จเรียบร้อยแล้ว และ rest ที่เป็น lazy-seq อีกตัว ประมาณนี้

(cons row (lazy-seq …))

เพราะว่าส่วนหลัง (rest) มันเป็น lazy sequence อยู่ก็เลยยังไม่ได้คำนวณจริง ๆ ก็เลยทำให้โปรแกรมมันกลายเป็นแบบ lazy ไปแล้ว (take 3 (distinct-id (map list (range) (range)))) มันก็ทำงานได้โดยเรียก body แค่ 3 ที

แอบดู Redox OS

ผมสนใจ Redox OS เพราะว่ามันใช้ Rust เขียน แต่ก็เกรง ๆ ว่ามันเป็น micro kernel จะใช้ได้จริงหรือเปล่า ลองเข้าไปลุย ๆ เปิด code ดูก็งงนิด ๆ เพราะว่าต่างจาก NetBSD ที่เคยดู ๆ มาเยอะเหมือนกัน

เคยเอามาลองเล่น ๆ แล้วมันก็ใช้งานได้แล้วบน QEMU ดูมีความหวังเรืองรองเลย

ก็เลยไปลองดูเอกสารรู้สึกแปลกว่าใจว่าเอกสารดีเกินคาดอธิบายไว้เลยว่าทำไมใช้ Rust ทำไมไม่โม Linux Minix หรือ BSD ที่ประทับใจกว่ามันช่วยไล่ code ได้เลย บอกเลยว่าไฟล์แรกที่ใช้คือ bootsector.asm ใน code นี่ชัดเลยว่า X86 เท่านั้น แล้วไฟล์ต่อมาคือ kernel/main.rs เป็น Rust แล้วดู code ก็ไม่งงมาก แต่ก็มี X86 asm ปนมาหน่อย ๆ แต่ก็คงไม่เป็นไรเอาให้มันใช้ได้ก่อนก็ดี แล้วค่อย port ไป CPU อื่น

นอกจากที่ว่ามาแล้วมันเป็นระบบแบบทุกอย่างเป็น URL คงขยายต่อมาจาก unix ที่ว่าทุกอย่างเป็นไฟล์

แต่ก็ยังส่องดูได้เท่านี้ล่ะครับ แต่ก็ตัดสินใจแล้วว่าสำหรับ OS เพื่อความบันเทิงก็จะตัดใจจาก NetBSD สักทีแล้วมาเล่น Redox แทน

Counting if err != nil in source codes written in Go

I wonder whether if err != nil is actually use in source code of a popular project written in Go programming language.

So I obtained runc including libcontainer from github:

git clone https://github.com/opencontainers/runc.git

I counted these following things:

  1. Number of source code files
    $ find runc -name '*.go' | wc -l
    359
    
  2. Number of lines
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | wc -l
    61209
    
  3. Number of “if err”
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | grep 'if err' | wc -l
    1684
    
  4. And finally, number of “if err != nil”
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | grep 'if err != nil' | wc -l
    1006
    

1006 of “if err != nil” from 359 files with 61209 lines. So I suppose that using if err != nil in Go-based project is common.

docker ท่าที่ผมใช้

ผมมีปัญหาว่า server ยังเป็น version ในอดีต แต่เครื่อง laptop กลับใช้รุ่นใหม่ล่าสุดทำให้ โปรแกรมที่บน server ได้บางทีก็ใช้บน laptop ไม่ได้ และในทางกลับกัน ก็เลยใช้ docker ดูแต่ว่าจะใช้ทีไรลืมคำสั่งทุกทีก็เลยจะจดไว้ในนี้

sudo docker run -it -v ‘/host/path:/container/path’ image-name

สมมุติว่า image-name เป็น php เราก็สามารถไปสั่ง php -S 0.0.0.0:8000 ใน /container/path ได้เลย แล้วก็เข้าเว็บจาก http://172.17.0.2:8000

ผมไม่รู้ว่าเป็นท่าที่ดีหรือเปล่า แต่ก็ช่วยให้งานเว็บของผมง่ายขึ้นเยอะเหมือนกัน

ลองให้ Firefox อ่านโพสออกเสียงให้ฟัง

บน Firefox บน GNU/Linux นี้พูดตรง ๆ เสียงอ่านแรก ๆ ก็ฟังยากเหมือนกัน แต่ฟัง ๆ ไปก็ชักรู้เรื่อง มี Highlight ให้ด้วยว่าอ่านไปถึงไหนแล้ว สะดวกดีเหมือนกันครับ

reader

เขียน Clojure บนเว็บในเครื่องเอง

ลองเล่น nightlight ดูรู้สึกว่ามันเจ๋งเหลือเกิน เพราะว่า

  1. มันง่าย Emacs นี่กว่าจะลง cider และ nrepl ให้มันต่อกันสำเร็จก็นานเหมือนกัน Cursive นี่จนถึงตอนนี้ก็ยังใช้มันไม่เสร็จ แต่ Nightlight นี่เพิ่ม plugin สั่ง lein เปิดเว็บเสร็จเลย
  2. InstaREPL สะดวกดีอยู่ ๆ ก็สั่ง run ให้หมดเลย มีผลให้ดูทางซ้ายเลย แต่ก็เจอปัญหาเหมือนกันเวลาเจอคำสั่งที่ทำงานช้า ๆ timeout ซะงั้น😛
  3. เล่นสีที่วงเล็บ สามารถ match วงเล็บตามสีได้เลยงงน้อยมาก ๆ

nightlite