ลอง Mithril.js

หลังจากเขียน Vue ไปเกือบเสร็จ มารู้ว่า Vue จะทำ hook โดยเฉพาะที่ Vue 3 อยากจะเปลี่ยน API เป็น function หมดแล้วโดนด่าต้องถอยสุดซอย การันตีว่าจะไม่เปลี่ยนอีกใน Vue 3.x (แล้ว 4 ล่ะ?) ก็เลยว่าจะ rewrite

สรุปว่า Project นี้เปลี่ยน framework ไปแล้ว 3 ตัว คือ Svelte (รีวิวไว้ใน Facebook ตั้งแต่ Svelte 3 ออก), Vue และ Mithril

Mithril นี่ claim มันคือมันบอกว่า API design มันคิดมาถูกตั้งนานแล้ว React อ่ะ design มั่วถึงต้องเปลี่ยนไปเปลี่ยนมา

แถม bundle size เล็กมาก 10kB เท่านั้น แล้วไม่ใช่แค่ view library อย่างเดียวแต่ใน 10kB นี้รวม router, promise polyfill และ XHR library แล้วด้วย!! แล้วเค้าก็ขายต่อว่า เพราะโค้ดมันเล็ก มันเลย review ได้ง่ายว่าปัญหาอยู่ตรงไหน performance ก็จะดีตาม

ส่วนว่าทำไม Mithril มีมาตั้งแต่ 6 ปีแล้วแต่ไม่ดัง บางคนบอกว่า “อ๋อ คนเขียน Mithril เค้าไม่ว่างเชียร์ พอดีกำลังเพลิดเพลินกับการเขียนโค้ดอยู่”

เอาสิ คนเขียน Vue ยังไม่ขิงขนาดนี้อ่ะ

Reactivity

View library ยุคใหม่ๆ จะเป็น Declarative หมด ก็คือเราบอกว่า interface หน้าตาแบบนี้ แล้ว library จะไปหาทาง update UI บนหน้าจอให้ตรงกับสิ่งที่เราบอกเอง

แต่สิ่งที่แสดงบนหน้าจอมักจะ depend กับ state เมื่อ state เปลี่ยนต้อง rerender interface ใหม่ ซึ่งวิธีที่แต่ละ Library ทำก็จะแตกต่างกันออกไป…

  • AngularJS 1 ใช้ dirty checking โดยมันจะแอบ clone state เก่าไว้ พอเราแก้มันจะ diff state เก่ากับใหม่เพื่อดูว่า state ตรงไหนเปลี่ยน แล้ว view ตรงไหน depend กับ state นี้
  • MobX 4 กับ VueJS 2 ใช้วิธี rewrite ทุก field บน state object ให้มี getter setter เพื่อที่จะได้ track การใช้งานได้ว่ามีการ set field นี้ แล้วใคร get มันบ้าง
  • MobX 5 กับ VueJS 3 ใช้ Proxy แทนการ rewrite field
  • Svelte ใช้ compiler rewrite field assignment ให้ไป mark ว่า field นี้มีการเปลี่ยนแปลง
  • React บังคับให้ state เป็น Immutable เพื่อให้ set state ได้ทางเดียว เมื่อ set แล้วก็จะ rerender component ที่เปลี่ยน state เพราะไม่รู้ว่าตรงไหนใช้อะไรบ้าง
  • Redux ใช้ Immutable state เช่นกัน เมื่อมีการ dispatch action แล้ว listener จะยิงให้ view library refresh

คำตอบของ Mithril คือ Mithril ไม่มี reactivity

ถ้าใครเขียน AngularJS 1 หรือ MobX จะรู้ว่าการเปลี่ยนค่าตัวแปรจะต้องรันในฟังค์ชั่นที่อนุญาตเท่านั้น ไม่อย่างนั้นมันอาจจะไม่อัพเดต ใน angular เรียกว่า $apply หรือ MobX เรียกว่า action เมื่อออกจาก action แล้ว library จึงจะเช็คว่าต้องแจ้งใครให้อัพเดตตามบ้าง

ใน Mithril โค้ดก็จะคล้ายๆ กัน แต่ไม่ต้องครอบฟังค์ชั่น เพียงแค่เรียก m.redraw() หลังจากแก้ state เสร็จแล้วเท่านั้นเอง แล้วมันจะ rerender ทั้งหน้าเว็บหลัง requestAnimationFrame

แล้วจุดที่มักจะมี state change ใน Angular เองก็จะครอบ $apply ให้ ใน Mithril ก็จะทำให้เช่นกัน ได้แก่ event handler และ หลัง AJAX call (ถ้าใช้ m.request)

ผมไม่แน่ใจว่าผมรู้สึกยังไงกับฟีเจอร์นี้ การทำ fine grained update แบบ Angular อาจจะฟังดูเร็วกว่าแต่มันก็มี CPU overhead และเพิ่ม code size ที่ต้อง track change ในขณะที่การรีเฟรชทั้งหมดก็เปลือง CPU

Global

State ของ Mithril เป็น Global เกือบทั้งหมดจนผมกังวลใจ

เช่น ฟังค์ชั่น m.redraw() ที่เล่าไปด้านบน นั่นคือ Signature เต็มๆ ของมันแล้วเพราะ Mithril เก็บ state เป็น global ไม่ต้องบอกว่าจะ redraw อะไร

หรือ Mithril router เองก็ใช้ m.router.get() อ่านค่า Route ล่าสุดโดยไม่ต้องพยายามหา router instance

เรื่องนี้คิดว่าเป็นเรื่องหนึ่งที่ยากจะกลืน พอๆ กับที่ React เอา HTML กับ JavaScript ผสมกัน (คนเขียนเว็บยุคเก่าจะบอกว่ามันผิด Separation of Concern) แต่คิดว่าที่ออกแบบมาแบบนี้เพราะ Mithril ไม่ได้คิดเรื่อง server side rendering เท่าไรนัก โค้ดที่ใช้ทำ SSR เองก็อยู่นอก codebase หลักของ Mithril

ผมก็เลยต้องถามว่าแล้วมันจะ get router state บน server ยังไงให้มัน thread safe เพราะถ้ากำลัง fetch data อยู่แล้วอีก request เข้ามา route มันก็จะเปลี่ยน

ปรากฎว่า ใน project ตัวอย่างมันใช้ Mithril router ใน clientside อย่างเดียว บน server มัน convert route definition ไปรันบน Express router

ก็เป็นประเด็นหนึ่งที่น่ากังวลถ้าต้องทำ server side rendering เพราะมันทำได้ แต่อาจจะต้อง if แยกไปพอสมควร สำหรับใน project นี้แล้วไม่มี server side rendering ก็เลยยังไม่ต้องห่วงเท่าไร

Component

เล่าทฤษฎีเสียนาน เข้าประเด็นดีกว่าว่า Component ใน Mithril หน้าตาอย่างไร

Component ใน Mithril เป็น JavaScript Object ธรรมดา ซึ่งก็น่าสนใจดีเพราะมันไม่ใช่ class ที่จะมี constructor และมันไม่ใช่ function ที่จะไม่มี state

const component = {
    view() {
        return m(".text", ["Hello world"]);
    }
}

ในทางปฏิบัติเหมือนว่า Mithril จะทำให้ component มี state อยู่ดีและเราสามารถเก็บข้อมูลไว้ใน this ได้ หรือใช้เรียกเมธอดอื่นๆ บน component

ฟังค์ชั่น JSX ของ Mithril คือ default export ของ Mithril เองซึ่งมักจะนิยมตั้งเป็น m

import m from 'mithril';

แต่ Mithril ไม่ค่อยจะเชียร์ JSX เท่าไรนักนอกจากบอกว่ามันก็ทำได้ เพราะ m จริงๆ นอกจากจะระบุ parameter แรกเป็น HTML Tag หรือ component ได้แล้วมันยังสามารถระบุ CSS Selector ได้ด้วย เรียกว่า Hyperscript เช่น m("input.input-lg[type=text][name=username]") จะได้ผลเป็น <input class="input-lg" type="text" name="username"> (กรณีที่มี dynamic attribute ก็ยังใส่ใน parameter ที่ 2 ของ m ได้ตามปกติเหมือน JSX ทั่วไป)

การใช้งานจริงผมพบว่า Prettier ไม่เข้าใจ Hyperscript และทำให้โค้ดอ่านค่อนข้างยาก เช่น โค้ดนี้

const component = {
    view() {
        return m("form.main", {
            onsubmit: onSubmit,
        }, [
            "Username: ",
            m("input[type=text][name=username]"),
            m("br"),
            m("input[type=submit][value=Submit]"),
        ]);
    }
}

จะโดน Prettier จัดเป็น

const component = {
    view() {
        return m(
            "form.main",
            {
                onsubmit: onSubmit,
            },
            [
                "Username: ",
                m("input[type=text][name=username]"),
                m("br"),
                m("input[type=submit][value=Submit]"),
            ]
        );
    },
};

ทำให้โค้ดค่อนข้างเทอะทะ อ่านยาก ทาง Prettier เองก็ไม่อยากให้มี Framework-specific formatting เท่าไรแต่อาจจะมีระบบ Plugin ในอนาคต

ในขณะเดียวกันเองถ้าจะเลี่ยงปัญหานี้ไปเขียน JSX เลย ก็ยังรู้สึกว่า m syntax สะดวกในการเขียนมากกว่า อย่างหนึ่งคือมันแยก static attribute กับ dynamic attribute ออกจากกันชัดเจน ก็ยังตัดสินใจไม่ค่อยได้เท่าไรว่าใช้แบบไหนดี ไม่อยากผสมกันหลายๆ ท่าเพราะมันจะเหนื่อยตอน convert เมื่อ component เริ่มเปลี่ยนไปแล้วอีกแบบเหมาะกว่า

Local State

ผมดองบล็อคเรื่องว่า React hook มันผิดยังไงมานานแล้ว สั้นๆ คือ Function ไม่มี State แต่ปรากฏว่า React hook call แต่ละครั้งได้ผลไม่เหมือนกัน แปลว่ามันต้องมี Global state สักที่

แล้ว React ก็ใส่ magic เข้าไป ทำให้ state แทนที่จะเป็น Global ก็ขึ้นอยู่กับ call site ได้ แต่เมื่อ call site เปลี่ยนมันก็พัง รวมถึงไปเรียกนอก React ไม่ได้

คำถามคือ แล้ว useState() ที่ไม่ Magic หน้าตายังไง? ผมก็ไม่รู้เหมือนกัน จนกระทั่งมาเห็นท่าที่ Mithril ทำ

function factory() {
    let counter = 0;

    return {
        view() {
            return m("div", [
                counter,
                m("input[type=button]", {
                    onclick: () => {
                        counter++
                    }
                })
            ])
        }
    }
}

ใช่! ใช่! ใช่!

วิธีที่เป็น Idiomatic ของ JavaScript ในการทำ local state ก็คือ Closure กับ class attribute นี่แหละ

เวลาใช้งาน function นี้เราไม่ต้องเรียก factory function เองแต่ Mithril ยอมรับ component factory เป็น component ด้วย แล้วมันจะไป manage instance ให้เอง

Final Thoughts

ผมคิดว่า Mithril ยังไม่ตอบโจทย์อยู่ 2 เรื่องที่ framework อื่นๆ ถือเป็นเรื่อง Hot มาก คือ Server rendering ซึ่งตามมาด้วยประเด็นว่า Global state ใน Frontend มันกลายเป็นอดีตไปแล้วเมื่อมันต้อง render บน client side

ถ้าตัด Server render ออกแล้วใช้ Global state คิดว่ามันก็น่าจะใช้ง่ายดี อาจจะตัด state management library ไปได้ด้วย แต่ถ้าทำแบบนั้นจะเอาไปเทียบกับเฟรมเวิร์คอื่นๆ ก็จะไม่แฟร์เท่าไร

Claim ที่แฟน Mithril เขียนมันผมก็เลยยังตามหาไม่เจอว่าแล้วมันจริงหรือเปล่า เค้ามองเห็นอะไรที่ผมยังไม่เจอหรือเปล่า และเค้าทำเว็บโดยไม่ใช้ SSR จริงหรอ

Hard things in VDOM Framework

หลังจากดู Framework มาหลายตัว คิดว่าจุดที่ควรจะศึกษาจาก Framework แต่ละตัวคือ

Routing

เท่าที่เขียนเว็บมา Routing จะมีอยู่สามแนวคือ

  1. Router เป็น component ที่ render subtree ตามหน้า ท่านี้จะเหมือนกับ React Router 4
  2. Router เป็น state manager แล้วเราต้อง check state และ render เอง ท่านี้เจอใน Router5
  3. Router อยู่ข้างนอกแล้ว Manage root component ตามที่ใช้ อันนี้เคยเขียนเองใน abstract-state-router หรือ Mithril router ก็จะทำท่านี้

ตอนนี้ยังเอนเอียงอยู่ระหว่าง 2 กับ 3 ว่าท่าไหนเป็นท่าที่ถูก ข้อดีของ Router5 คือมันทำ subroute แล้วให route หลัก render ส่วนนึง subroute render อีกส่วนได้ แต่ใน Vue อาจจะใช้ Slot API ทำท่านี้แทนได้โดยไม่ต้องพึ่ง router?

อีกปัญหาหนึ่งที่ยากของ Router คือ Fetch data ก่อนที่จะ Navigate ซึ่งเหมือนกับที่ Browser ทำอยู่ ก็แปลกใจมากที่ React Router 4 เลิก support ท่านี้ไปแล้วให้ไปทำที่ Link component แทน (กลายเป็นว่าคนใช้ต้องรู้ว่าจะ link ไปหน้านี้ต้องใช้ component ไหน) คิดว่า React suspense ออกมาเพื่อแก้ปัญหานี้ ส่วน Framework อื่นๆ ยังไม่ได้สนใจตรงนี้

State management

State ใน web framework เท่าที่เห็นจะมีอยู่สามแบบคือ

  1. Local state เช่น form เพราะถ้า hoist มันขึ้นมันจะช้า (แต่ก็มีคนทำอยู่บ้าง พวก Redux form)
  2. Scope state คือ local state แต่ pass through ลงไปให้ component ลูกด้วย บางที scope อาจจะใหญ่มากจนถึงทั้งหน้า
  3. Global state คือ state ที่ share ระหว่างหน้า หรือระหว่าง component ที่ไม่ได้เป็น parent-child กัน

ที่น่าจะดูคือเราสามารถ Pass object จากด้านบนลงไปด้านล่างแบบ implicit ได้ไหม (แบบ React context API) เพราะการใช้ global variable ไม่ดีต่อ server render ในขณะเดียวกันการ pass ทุกอย่างเป็น explicit ก็ทำให้ component ที่ไม่อ่าน state ก็ต้องรับ state ไปเพื่อส่งต่อด้วย (นอกจากนั้นอาจจะเจอปัญหาว่าข้างบนไม่ได้รับ state ข้างล่างจะใช้ไม่ได้)

บาง Framework อาจจะมี state management มาในตัวเลย (หรือมีตัวที่ค่อนข้างแนะนำ) อันนี้ก็ต้องลองดูเหมือนกันว่ามันเวิร์คมั้ย เช่น Angular $scope หรือ Vuex (ที่ไม่ได้มาด้วยกันแต่แนะนำมากๆ)

Reactivity

พูดถึง state แล้วสิ่งที่ตามมาคือเมื่อ state เปลี่ยนแล้วมันจะรู้ได้ยังไงว่าต้อง rerender อันนี้ด้านบนอธิบายไปแล้ว เลื่อนขึ้นไปอ่านได้

Code Splitting

อย่างน้อยๆ ก็ใน Router ก็มักจะได้ใช้ Code splitting แล้วใน Page อาจจะแยกต่อไปอีกก็ได้

บาง Framework เช่น Vue รองรับ component ที่คืน Promise มาได้เลย เห็นว่า React Suspense ก็น่าจะมา ส่วนบาง library ที่บ้านๆ มาหน่อยอาจจะต้อง manage Promise state เอง

ฟังดูเหมือนไม่ยาก แต่ถ้าวาด state machine มาจะพบว่ามันมีหลาย state กว่าที่คิด

  1. Initial (บาง framework เช่น React side effect จะเกิดขึ้นได้เฉพาะหลัง mount แล้ว)
  2. Loading แต่ไม่แสดง indicator
  3. Loading และแสดง indicator (ปกติถ้าโหลดเร็วมากๆ ไม่ขึ้น indicator เลย คนจะรู้สึกว่าเว็บเร็วกว่า)
  4. Load สำเร็จแล้ว ไปแสดง component จริง
  5. Load ไม่สำเร็จ แสดง error component

และบน server render มันอาจจะต้อง force ให้มันโหลด component ทันทีก่อนคืนให้ client

Fragment

บางที component ย่อย/output ของ loop อาจจะจำเป็นต้องคืนสมาชิกหลายตัวโดยไม่มีอะไรครอบ โดยเฉพาะเมื่ออยู่ภายใต้ <table> หรือ <ul> ตัว framework ควรจะอนุญาตให้คืนหลายๆ child component ได้

หรือบางที component อาจจะไม่อยาก render เลยถ้าไม่มีอะไร เช่น error display ถ้าไม่มี error ก็ไม่ต้องคืนอะไร

บาง library เช่น domvm ใช้แนวคิดว่า virtual DOM map 1-1 กับ DOM จริง ก็เลยจะไม่ support use case ข้างบนนี้ ต้องทำ div ขยะไว้บนหน้าแทน

Portal

โดยปกติแล้วในเว็บเราจะใช้ element ซ้อนกันไปได้เรื่อยๆ แล้วก็ใช้ CSS จัดหน้าได้ไม่มีปัญหา แต่ CSS เช่นกันที่ทำให้เราซ้อน element ที่ไม่เกี่ยวข้องกันไม่ได้เพราะ style ของ parent มีผลต่อ child ด้วย และบางอย่าง child override ไม่ได้ เช่น z-index ที่ยุ่งยาก ไปจนถึง position: relative แล้วมี position: absolute ซ้อนด้านใน

requirement ที่จะเจอบ่อยมากคือ Modal window ซึ่งในแอพง่ายๆ อาจจะยังไม่เจอหรือขี้เกียจเขียนแล้วใช้ prompt/alert ไปก่อน แต่พอจะทำดีๆ แล้ว framework บางตัวไม่มีคำตอบให้ว่าตอนนี้อยู่ข้างในจะโผล่ Modal ออกมาได้ยังไง

ท่า hackๆ หน่อย ก็อาจจะเป็นว่ามี component ตัวรับอยู่ด้านนอกแล้ว component ด้านในส่งสัญญาณไปหาตัวนอกว่าช่วย render อันนี้ตรงด้านนอกให้หน่อย ซึ่งมัน break การแยก component (ทำไม component ตัวนอกต้อง add portal receiver ที่ตัวเองไม่ได้ใช้) แล้วอาจจะมี hack เข้ามาเกี่ยวข้อง (component ด้านในคุยกับตัวด้านนอกยังไง? แล้วมันส่ง vdom ออกไปได้ยังไง?)

คำถามถัดมาคือแล้ว event bubbling จะทำงานยังไง? ถ้า parent ดัก click event ทั้งหมดไว้แล้วเราคลิกบน portal, parent ควรจะได้ไหม? เพราะ DOM จริงไม่ส่งมาเพราะมันไม่ได้เป็น parent-child กันแล้ว แต่ใน Virtual DOM มันยังเป็น parent-child กันอยู่นะ

CSS Class

คิดว่าการผสม CSS Class เป็นอะไรที่ใช้บ่อยมากถ้าใช้ CSS Module บาง framework เช่น Vue ก็ทำได้เลย แต่ถ้าใช้ React หรือ Mithril ก็ยังต้องใช้ classnames helper

บางคนก็จะใช้ CSS-in-JS ไปเลยซึ่งเวลาสั่งให้มันผสมมันจะทำ class ใหม่มา

Error Boundary

สุดท้ายเรื่อง Error ก็สำคัญกับ UX ที่ดี เราควรจะทำให้เว็บ partial failure ได้ว่าถ้า component ด้านในพัง ด้านนอกจะ render error ให้ user ได้ ซึ่งเราไม่สามารถ try..catch ใน render function ได้ (เพราะเราคืน Virtual DOM nodes ไปก่อน แล้ว vdom library เป็นคนเรียก component ด้านใน) ดังนั้น Virtual DOM ต้องจัดการเรื่อง error ให้ด้วยว่าถ้าข้างใน fail ต้องกลับมาให้ด้านนอก catch ได้อยู่

Wall of Text #12: File Extension is Magic

วันนี้มีรูปมาให้ดู

ถามว่ารูปนี้มีอะไรพิเศษ แนะนำให้ลองเซฟรูปดูแล้วจะเห็นว่ามันเป็น JPG และอย่างที่รู้กันว่า JPG มันหมุนไม่ได้

🤔

ที่เป็นอย่างนี้ก็เพราะว่า นามสกุลไฟล์เป็นเพียงข้อเสนอแนะ ใน Windows ระบบจะใช้นามสกุลไฟล์เพื่อบอกว่าจะต้องเปิดไฟล์นี้ด้วยโปรแกรมอะไร แต่ใน Linux แล้ว ไฟล์หลายๆ ประเภทระบบสามารถระบุประเภทของไฟล์ได้เองแล้วแม้ไม่ต้องใส่นามสกุลไฟล์!

เวลาที่เราแปลงนามสกุลไฟล์ เราถึงจะต้องไปเปิดในโปรแกรมขึ้นมาแล้วเซฟใหม่เป็นนามสกุลใหม่ ถึงจะเปิดได้ถูกต้อง เช่นถ้าเรามีไฟล์ MKV อยู่แต่โทรทัศน์ของเรารองรับเฉพาะไฟล์ MP4 เราก็ต้องใช้โปรแกรม Handbrake แปลงก่อนถึงจะไปเปิดในโทรทัศน์ได้ เพราะการเปลี่ยนชื่อไฟล์ไม่ได้เปลี่ยนเนื้อหาในไฟล์

(ไฟล์ thinking.jpg ด้านบน ก็สร้างมาจากการ rename thinking.gif เป็น thinking.jpg เฉยๆ โดยไม่ได้ผ่านโปรแกรมใดๆ)

It’s Magic

แล้ว Linux ทราบได้อย่างไรว่าจริงๆ แล้วมันคือ GIF ไม่ใช้ JPG? คำตอบคือ It’s magic!

ถ้าเราลองเปิดไฟล์รูปที่เซฟมาใน Notepad หรือโปรแกรมแก้ไขข้อความอื่นๆ เราจะเห็นแบบนี้

จะสังเกตว่าไฟล์นี้จะขึ้นต้นด้วย GIF89a ซึ่งมาตรฐานของไฟล์ GIF กำหนดไว้ให้เป็น “Magic number” ของไฟล์ GIF ทั้งหมด

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

(นอกจาก GIF89a แล้ว GIF ยังมี GIF87a อีกด้วยซึ่งเป็นมาตรฐานเก่า โดย 1989 และ 1987 เป็นปีที่มาตรฐานนั้นออกมาครั้งแรก)


ไม่ใช่ทุกไฟล์เสมอไปที่จะมีเครื่องหมายเป็นภาษามนุษย์อ่านง่ายแบบนี้ การเขียนโปรแกรมเองก็ไม่ได้มีกฎหมายอะไรที่บอกว่าไฟล์จะต้องมีเครื่องหมายบอกไฟล์เสมอไป ทำให้แต่ละไฟล์มีจุดบอกตำแหน่งแตกต่างกันออกไป เช่นไฟล์ doc (Microsoft Word รุ่นเก่า) นั้นเมื่อเปิดในโปรแกรม Hex Editor จะขึ้นต้นด้วย D0 CF 11 E0 A1 B1 1A E1 ซึ่งถ้าใช้โปรแกรม Notepad ทั่วๆ ไปอ่านจะเป็นตัวยึกยือ

เพื่อให้การจำแนกไฟล์ได้ง่ายขึ้นและเรียกจากโค้ดได้ จึงมีผู้รวบรวมข้อมูลเหล่านี้เป็นฐานข้อมูลไว้และทำเป็นโปรแกรมออกมาชื่อ file (ตรงไปตรงมาดี) เนื่องจากโปรแกรมนี้ออกมาพร้อมกับ UNIX แล้วทำให้ลินุกซ์ทุกตัวติดตั้งโปรแกรม file ได้หมด วิธีการใช้งานใช้งานเพียงพิมพ์ file ตามด้วยตำแหน่งของไฟล์ปริศนา

$ file thinking
thinking: GIF image data, version 89a, 498 x 498

นอกจากจะบอกว่าเป็นไฟล์ประเภทใดถูกแล้ว สำหรับไฟล์ GIF โปรแกรมยังบอกขนาดได้อีกด้วย!

Standing on the shoulder of giants

ภาษาอังกฤษมีสำนวนว่า “ยืนบนไหล่ของยักษ์” หมายถึงว่าความก้าวหน้าต่างๆ เกิดขึ้นจากการต่อยอดจากความก้าวหน้าเดิม

ในโปรแกรมมิ่งก็เช่นเดียวกัน เวลาเขียนโปรแกรมเราก็มักจะใช้ Library สำเร็จรูป เช่น ถ้าเราจะส่งข้อมูลเราระหว่างโปรแกรมเราอาจจะใช้ฟอร์แมต JSON จะได้ไม่ต้องคิดค้นขึ้นมาใหม่

ฟอร์แมตไฟล์ต่างๆ ก็เช่นเดียวกัน หลายๆ ฟอร์แมตเมื่อเราเปิดมาแล้วก็อาจจะพบว่ามันมี Magic ของไฟล์ประเภทอื่นอยู่ เช่นถ้าเราเปิดไฟล์ docx (Microsoft Word 2007+), jar (โปรแกรม Java), APK (แอพ Android), IPA (แอพ iOS) ขึ้นมาจะเห็นว่ามันขึ้นต้นด้วย PK ซึ่งเป็น Magic ของไฟล์ Zip และสามารถใช้โปรแกรมเปิดไฟล์ Zip เปิดได้ด้วย

docx

ไฟล์เหล่านี้บางประเภทก็สามารถใช้โปรแกรม file ตรวจสอบได้เช่นกัน

$ file file-sample
file-sample: Microsoft Word 2007+

Next Time

Video – Free HD Stock-Footage and Motion Graphics by CyberWebFX https://www.youtube.com/c/CyberWebFX
Music – Floaters – Jimmy Fontanez CC-BY 2.0

วิดีโอข้างบนขนาด 1MB ความละเอียด 480p ภาพก็ค่อนข้างโอเค เสียงเพลงก็โอเค

ส่วนวิดีโอนี้ในอัพโหลดในปี 2008

ความละเอียด 240p ขนาด 4MB

Only if technology could’ve come sooner