<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by W.S.Kiang on Medium]]></title>
        <description><![CDATA[Stories by W.S.Kiang on Medium]]></description>
        <link>https://medium.com/@bullrish?source=rss-59491a7bd191------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*0sJed-HL-cZ2saFd0PG1MQ.jpeg</url>
            <title>Stories by W.S.Kiang on Medium</title>
            <link>https://medium.com/@bullrish?source=rss-59491a7bd191------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 22 Jun 2026 12:00:13 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@bullrish/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Clojure for Curious People(1): introduction, Clojure compilation and anatomy of Clojure’s syntax]]></title>
            <link>https://medium.com/@bullrish/clojure-for-curious-people-1-introduction-clojure-compilation-and-anatomy-of-clojures-syntax-a577f575932a?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/a577f575932a</guid>
            <category><![CDATA[clojure]]></category>
            <category><![CDATA[geek]]></category>
            <category><![CDATA[compilation]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Sun, 25 Mar 2018 12:44:49 GMT</pubDate>
            <atom:updated>2022-03-24T04:08:38.399Z</atom:updated>
            <content:encoded><![CDATA[<h4>a ground up approach</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RWiZIu2gbf-fKsbudq83xQ.png" /><figcaption><a href="https://en.wikipedia.org/wiki/Oath_of_the_Horatii#/media/File:Jacques-Louis_David,_Le_Serment_des_Horaces.jpg">https://en.wikipedia.org/wiki/Oath_of_the_Horatii#/media/File:Jacques-Louis_David,_Le_Serment_des_Horaces.jpg</a></figcaption></figure><h4>Introduction:</h4><p>ช่วงนี้รู้สึกสนใจภาษา clojure มันดูมีความน่าสนใจอยู่หลายๆอย่าง (แล้วก็เงินเดือนนี่สูงปรี้ดเลย <a href="https://insights.stackoverflow.com/survey/2017">[1]</a>[<a href="https://insights.stackoverflow.com/survey/2018">21</a>] แค่นี้ก็จูงใจผมได้มากแล้ว) เป็นต้นว่า clojure เป็นภาษา functional ที่พัฒนาแนวคิดต่อมาจาก LISP <a href="https://clojure.org/">[2]</a> ฉะนั้นเราอาจเจอวงเล็บยั้วเยี้ย ซึ่งบางคนพอเจอวงเล็บแล้วก็รู้สึกไม่ดีเท่าไหร่ แต่จริงๆแล้วมันไม่ได้เป็นปัญหาขนาดนั้น พวกบรรดา text editor ก็ยังมีตัวช่วยให้เราเขียน clojure สะดวกขึ้นอย่างมากมาย เป็นต้นว่า อย่างผมที่เป็นสาวกของ atom มันก็มีตัวช่วยจัดการวงเล็บคือ package ชื่อ <a href="https://atom.io/packages/parinfer">parinfer</a> ความสามารถมันแบบประมาณว่ามันจะจัดการวงเล็บให้เราตาม indent แต่ถึงอย่างไรมันก็เกิดปัญหาได้ เพราะบางทีมันก็ฉลาดเกินไปจนไปจัดการวงเล็บที่เราไม่อยากให้ไปจัดการ หรือบางทีเราก็อยากเข้าไปสังคายนาตัวโค้ดในกลุ่มวงเล็บบางกลุ่ม ซึ่งก็มีตัวช่วยจัดการอีกเหมือนกันคือ <a href="https://atom.io/packages/lisp-paredit">lisp-paredit</a> แค่นี้ก็ถือว่าจัดการปัญหาเรื่องวงเล็บไปมากแล้ว ซึ่งทำให้เราไม่ต้องพะวงกับการนับวงเล็บอะไรแบบนี้อีกต่อไป</p><p>นอกจากนั้นใน atom ยังมี <a href="https://atom.io/packages/proto-repl">proto-repl</a> ที่เอาไว้ลอง experiment กับฟังก์ชันที่เพิ่งเขียนไป ทั้งนี้คำแนะนำคือให้ setup ตาม gist ลิ้งด้านล่างนี้ไปเลย ซึ่งครบถ้วนทั้งหมดทุกตัวที่กล่าวมา</p><blockquote><a href="https://gist.github.com/jasongilman/d1f70507bed021b48625">Atom Clojure Setup</a></blockquote><p>แต่โอเคถ้าคุณไม่อยากใช้ atom จริงๆแล้ว clojure มันดูไปกันได้กับ editor ตัวอื่นอีกมากมายอย่าง emac ผมแนะนำให้ลองดูการ setup เบื้องต้นจาก <a href="https://www.braveclojure.com/basic-emacs/">https://www.braveclojure.com/basic-emacs/</a> และ vsCode ก็มี <a href="https://spin.atomicobject.com/2017/06/22/clojure-development-with-visual-studio-code/">plugin</a> ตัวช่วยอยู่เหมือนกัน</p><p>สำหรับตัวจัดการ project หรือ package ในโลก clojure มีอยู่สามสี่ตัวที่น่าสนใจ แต่ที่ดูจะฮอทบวกฮิตมากที่สุดน่าจะเป็น <a href="https://leiningen.org/">Leininge</a>n ซึ่งผมเองก็คงแนะนำให้ใช้ตัวนี้เพราะ community ค่อนข้างจะใหญ่และดู stable มาระดับนึงแล้ว หลังจากติดตั้งไปแล้ว (ชาว os x สามารถใช้ <a href="https://gist.github.com/technomancy/2395913">brew install leininge</a>n ได้) เราก็จะสามารถสร้าง project ได้ง่ายๆด้วยคำสั่ง</p><pre>lein new your-app-name-here</pre><h4>Clojure is compiled to JVM bytecode:</h4><p><strong>Clojure</strong> จริงๆแล้วมันถูก compile เป็น jvm (<a href="https://en.wikipedia.org/wiki/Java_virtual_machine">java virtual machine</a>) bytecode [<a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/">3</a>][<a href="https://www.braveclojure.com/getting-started/">4</a>] แต่นั่นไม่ได้หมายความว่า clojure มันจะแปลงไปเป็น java ก่อนแต่อย่างใด แต่มันกลายเป็น java bytecode เลย อันที่จริง jvm ไม่รู้จัก java ด้วยซ้ำ มันรู้จักแค่ java bytecode เท่านั้น <a href="http://blog.ndk.io/clojure-compilation.html">[6]</a> โดยตัวที่ jvm นั้นจะอ่าน (interpreted) ขอแค่อยู่ในรูป jvm bytecode <a href="https://en.wikipedia.org/wiki/Interpreted_language">[18]</a> ซึ่งจริงๆแล้วอยู่ในฟอร์แมต <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html"><strong>Java Class</strong> <strong>File Format</strong></a> (ตัวอย่าง อื่นๆก็ scala, Kotlin) ตัวอย่างสามารถดูได้จากรูปด้านล่าง <a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/">[3]</a><a href="https://en.wikipedia.org/wiki/List_of_JVM_languages">[5]</a><a href="http://blog.ndk.io/clojure-compilation.html">[6]</a> นั่นทำให้ function ใน clojure เวลา compile สุดท้ายจะกลายเป็น .class<a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/">[3]</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*FpLLYxSX2QrV3atphjWvAQ.png" /><figcaption>Java Class File, src: <a href="http://blog.ndk.io/clojure-compilation.html">http://blog.ndk.io/clojure-compilation.html</a></figcaption></figure><p>ถึงตรงนี้ จริงๆแล้วอธิบายเวิ่นเว้อไปมาก สามารถสรุปได้ดังนี้</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/661/1*iHZ0k7-dofTnlmVm3YXb7w.png" /><figcaption>Flow of Clojure to JVM [6][18]</figcaption></figure><p>(เพิ่มเติม: เราอาจบอกว่า java bytecode ก็คือชุด class ไฟล์หลายๆไฟล์รวมกันซึ่งแต่ละไฟล์ข้างในก็จะมีชุด instructions <a href="http://blog.ndk.io/clojure-compilation.html">[6]</a>)</p><p>เราสามารถลอง compile ได้ด้วย</p><pre>lein new app<br>cd app<br>lein compile app.core<br>ls target/classes/app</pre><p>เราจะได้ไฟล์ 4 ไฟล์ หน้าตาประมาณนี้ ตัวเลขอาจไม่เป๊ะ พวกนี้คือ java byte code ที่ได้จากคำสั่ง lein compile app.core</p><pre>core$fn__38.class                    <br>core$foo.class                       core$loading__5569__auto____36.class <br>core__init.class</pre><p>ตรง <strong>core$foo.class</strong> จริงๆแล้วมันคือ ฟังก์ชัน foo ที่ถูกสร้างขึ้นอยู่ใน src/app/core มาตอนที่เราใช้คำสั่ง lein new appพอมันถูก compile มันจึงกลายเป็น .class ถ้าเราลองสร้างฟังก์ชันอื่นๆใน core.clj จากนั้นเรา compile อีกที มันก็จะโผล่มาใน target/classes/app เป็น core$anyFunction.class นั่นเอง เราสามารถทำการส่องในแต่ละไฟล์ได้ด้วย javapเช่นสมมุติเราจะส่อง core$foo.class หรือส่อง core__init.class</p><pre>javap -c target/classes/app/core\$foo.class<br>javap -c target/classes/app/core__init.class</pre><p>เราจะเห็นข้างในเป็น JVM <em>assembly instruction </em><a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/"><em>[3]</em></a></p><p>ส่วนสามไฟล์ที่เหลือ</p><pre>core__init.class // core.clj loader class<br>core$fn__38.class // dosync anonymous function<br>core$loading__5569__auto____36.class // with-loading-context anonymous function</pre><p>สามารถอ่านเพิ่มเติมได้ใน <a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/">[3]</a><a href="http://blog.ndk.io/clojure-compilation.html">[6]</a></p><p>แต่ทีนี้ clojure มัน run แบบไม่ต้องผ่านการ compile แล้วได้เป็นไฟล์ออกมาแบบที่เราทำไปก็ได้ คือมันสามารถ dynamic compilation ได้ แบบเหมือนทำกลางอากาศ คือมันก็ได้ bytecode มาให้ JVM อยู่เหมือนกันเพียงแต่มันไม่ save เป็นไฟล์แบบตอนเราใช้ lein compile ซึ่งเป็น <a href="https://en.wikipedia.org/wiki/Ahead-of-time_compilation">ahead-of-time compilation</a> เราสามารถลอง dynamic compilation ผ่าน lein repl <a href="http://blog.ndk.io/clojure-compilation.html">[6]</a></p><h4><strong>Clojure is dynamic language</strong></h4><p>Clojure เป็นภาษา dynamic language ฉะนั้น โดยปกติเราประกาศตัวแปรแบบไม่มี type [<a href="https://clojure.org/">2</a>][<a href="http://www.joyofclojure.com/">7</a>] ตัวอย่างด้านล่างจะเป็นการประกาศตัวแปรต่างๆ</p><pre>(def n 100000) // ประกาศตัวแปร n ให้เป็น Number มีค่าเป็น100000<br>(def a &quot;blabla&quot;) // ประกาศตัวแปร a ให้เป็น String มีค่าเป็น &quot;blabla&quot;<br>(def f (fn [x] x)) // ประกาศ f เป็น function ที่รับ x แล้วคืนค่า x<br>(defn o [x] x) // ประกาศ function ชื่อ o รับ x แล้วคืนค่า x</pre><p>แต่มันก็ยัง strict เรื่อง type นะ คือเราไม่สามารถทำแบบนี้</p><pre>(+ 10 &quot;hello&quot;) // error<br>ทำได้เพียง<br>(+ 10 10) // 20</pre><p>อันแรกมันจะฟ้องว่าเรา add String เข้ากับ Number ไม่ได้ เราอาจกล่าวว่า clojure เป็นภาษาที่เป็น dynamic strong type (แต่โดยส่วนมองว่า definition ของ strong/weak นั้นยังเป็นที่ถกเถียงอยู่ [<a href="https://www.destroyallsoftware.com/compendium/types?share_key=baf6b67369843fa2">19</a>]) นอกจากนั้นเราใส่อะไรที่คล้ายๆ type ได้ เรียกว่า Type-hint ซึ่งจะเพิ่ม performance ได้ในกรณีที่มีการไปยุ่งกับ java (clojure นั้นจริงๆก็เป็นญาติกับ java) เราสามารถสังเกตได้ว่าอันไหนมันมาจากโลกของ java ด้วยการดู ‘.’ ว่ามันอยู่หน้าฟังก์ชันไหม ถ้าอยู่แสดงว่ามันคือ instance จาก java [<a href="https://clojure.org/reference/java_interop#typehints">8</a>] [<a href="http://clojure-doc.org/articles/language/functions.html">12</a>] เราใส่ type-hint เพื่อไม่ให้ ตอนที่มัน compile พวกตัวที่มาจาก java มันส่องหา type อยู่ตลอด เป็นการบอกใบ้นั่นเองว่า ไอ่ที่ส่งเข้าไปเนี่ยมันเป็น type อะไรกันแน่ [<a href="https://clojure.org/reference/java_interop#typehints">8</a>]</p><p>ซึ่งเอาเข้าจริงเราไม่ควรใช้ type-hint ตลอดเวลาหรือทั้งหมดทั้งโปรเจ็คเพราะมันก็คือการเพิ่มข้อมูลที่ส่งไปให้กับ compiler ด้วยเช่นกัน และ document แนะนำให้ใช้แค่ตอนที่เจอปัญหาแบบคอขวดแล้วรู้ว่าต้นตออยู่ตรงไหน [<a href="https://clojure.org/reference/java_interop#typehints">8</a>][<a href="https://www.safaribooksonline.com/library/view/clojure-programming/9781449310387/ch09s05.html">11</a>]</p><p>สำหรับด้านล่างนี้ถ้าเอาไป run แข่งกับแบบไม่ใส่ type-hint เช่นเอาไป run สักล้านรอบเราจะเห็นว่ามันไวขึ้นมาก ข้างล่างนี้จะเห็นว่ามันมี .length อันนี้มาจาก java แน่ๆ และเราใส่ type-hint เพื่อใบ้ compiler ว่ามันคือ string [<a href="http://www.joyofclojure.com/">7</a>][<a href="https://clojure.org/reference/java_interop#typehints">8</a>]</p><pre>(defn len [<em>^String</em> x] (.length x))</pre><h4>Anatomy of Clojure’s syntax:</h4><p>หากลอง lein repl จากนั้นลองแต่ละอันข้างล่าง พิมพ์แล้วกด enter ดูผลจะได้ว่า</p><pre>user=&gt; ()            // empty list, stay empty list<br>()</pre><pre>user=&gt; (def x 10)    // not empty list, x is 10<br>#&#39;user/x<br>user=&gt; x<br>10</pre><pre>user=&gt; (1 2 3)        // not empty list, get error (T_T)<br>ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn  user/eval1244 (form-init321820076331617300.clj:1)</pre><pre>user=&gt; (list 1 2 3)   // not empty list, get list<br>(1 2 3)</pre><p>เรารู้มาว่า clojure จะอยู่ในวงเล็บ แต่อะไรล่ะที่อยู่ในวงเล็บแล้วมันจะอ่านไม่พังไปซะก่อน (หรือพูดตรงกว่านั้นก็คือ eval ใน JVM แล้วไม่ error) ก่อนอื่นเราต้องทำความรู้จักกับวงเล็บก่อน จริงๆแล้ววงเล็บมันคือ data structure รูปแบบหนึ่ง อันที่จริงมันคือ Lists (ตัววงเล็บรุ่นพ่ออย่าง LISP ชื่อก็ย่อมาจาก lists processing [<a href="http://www.joyofclojure.com/">7</a>]) ซึ่งสิ่งที่จะทำให้ lists ใดๆ มัน eval แล้ว resolve (หรือก็คือเราพิมพ์ๆไปแล้วกด enter ใน repl แล้วมันทำงานไม่ขึ้น error) ก็ต่อเมื่อสมาชิกตัวแรกมันเป็นหนึ่งในด้านล่างนี้</p><pre><strong><em>- function<br>- macro</em></strong><em> <br></em><strong><em>- special form</em></strong></pre><pre>ex. (SOMETHING x 10)</pre><pre>SOMETHING should be one of those guy</pre><p>ถ้ามันเข้าเงื่อนไข สมาชิกตัวที่เหลือใน lists ก็จะกลายเป็น parameters ของไอ่ตัวแรกแบบเรียงลำดับทันที เช่น</p><pre>(+ 1 2 3 4)<br>function = +<br>parameter = 1 2 3 4</pre><p>[<a href="http://www.joyofclojure.com/">7</a>] จริงๆแล้วพอมีวิธีอย่างหยาบในการเช็คว่าอะไรเป็นอะไรอยู่บ้าง เช่นฟังก์ชัน list ที่จับ arguments มายัดเข้าไปให้กลายเป็น list พอพิมพ์ตรงๆไปที่ repl จะขึ้นเป็นแบบว่า</p><pre>user=&gt; list<br>#object[clojure.lang.PersistentList$Primordial 0x74866d26 &quot;clojure.lang.PersistentList$Primordial@74866d26&quot;]</pre><p>เราสามารถลองพิมพ์ตัวอื่นเล่นๆได้เช่น +, — , *, / ก็จะขึ้นหน้าตาคล้ายๆกันกับ list</p><p>ส่วน macro (เช่น <strong>defn, when</strong>) พอพิมพ์ไปตรงๆที่ repl จะได้แบบนี้</p><pre>user=&gt; when<br>CompilerException java.lang.RuntimeException: <strong>Can&#39;t take value of a macro</strong>: #&#39;clojure.core/when, compiling:(/private/var/folders/db/vnkkt7mx5wj84jxcwh4jplsw0000gn/T/form-init1239338506849356429.clj:1:1062)</pre><p>ส่วน special form(เช่น <strong>if</strong>) พอพิมพ์ไปตรงๆที่ repl จะได้แบบนี้</p><pre>user=&gt; if<br>CompilerException java.lang.RuntimeException: <strong>Unable to resolve symbol</strong>: if in this context, compiling:(/private/var/folders/db/vnkkt7mx5wj84jxcwh4jplsw0000gn/T/form-init1239338506849356429.clj:1:1062)</pre><p>ซึ่งจริงๆแล้ว ก็ไม่ได้เกี่ยวกันเสียทีเดียว และไม่เวิร์คอีกต่างหาก คือถ้าลองพิมพ์อะไรมั่วๆ มันก็ออกมาเหมือนพิมพ์ if</p><p>ทำไมมันถึงขึ้นว่า <strong>unable to resolve symbol</strong> แล้วอะไรคือ <strong>symbol</strong> สำหรับ clojure ? โอเคเดี๋ยวเราไว้มาตอบทีหลัง แต่ก่อนอื่นเราลองมาใช้ doc ดูจะเข้าท่ากว่า เราสามารถลองเช็คแต่ละตัวได้ด้วยแบบนี้</p><pre>user=&gt; (doc list)<br>-------------------------<br>clojure.core/list<br>([&amp; items])<br>  Creates a new list containing the items.<br>nil</pre><pre>user=&gt; (doc when)<br>-------------------------<br>clojure.core/when<br>([test &amp; body])<br><strong>Macro</strong><br>  Evaluates test. If logical true, evaluates body in an implicit do.<br>nil</pre><pre>user=&gt; (doc if)<br>-------------------------<br>if<br>  (if test then else?)<br><strong>Special Form</strong><br>  Evaluates test. If not the singular values nil or false,<br>  evaluates and yields then, otherwise, evaluates and yields else. If<br>  else is not supplied it defaults to nil.<br><br>  Please see http://clojure.org/special_forms#if<br>nil</pre><pre>user=&gt; (doc doc)<br>-------------------------<br>clojure.repl/doc<br>([name])<br><strong>Macro</strong><br>  Prints documentation for a var or special form given its name<br>nil</pre><p>! นอกจากจะพอบอกว่าแต่ละอย่างมันเป็นอะไรแล้ว มันยังบอก parameters และอธิบายคร่าวๆด้วยว่ามันเอาไว้ใช้ทำอะไร (นอกจากนั้นเราสามารถใช้ find-doc ซึ่งเอาไว้เสิร์ชหาสิ่งที่เกี่ยวข้องเช่นอยากหาฟังก์ชันหรือมาโครที่ทำงานเกี่ยวข้องกับ xor ก็สามารถใช้(find-doc xor) )</p><p>ทีนี้เรามาดูกันว่าจริงๆแล้ว function, macro กับ special form มันคืออะไรกันแน่?</p><h4><strong>Special form</strong></h4><p>ก่อนเราจะพูดถึง special form ซึ่งแน่นอนว่าเราต้องพูดถึง form ก่อน สำหรับ clojure แล้ว form คืออะไรก็ตามที่สามารถถูก eval ได้ฉะนั้น function, macro, special form ก็จัดว่าเป็น form [<a href="http://www.joyofclojure.com/">7</a>][<a href="http://clojure-doc.org/articles/language/macros">16</a>] แต่จริงๆแล้วก่อนที่จะ eval มันจะต้องผ่าน read ก่อน ซึ่งฟังก์ชัน read จะทำการอ่าน text (ที่จริงๆแล้วรับเข้ามาเป็น stream ศึกษาเพิ่มเติมได้ใน (doc read)) แล้วแปลงไปเป็น data structure (หรือโดยส่วนมากสำหรับโลก clojure ก็คือ list นั่นเอง)แล้วถูก eval อีกที โดยจริงๆแล้วตัว repl ก็ทำงานผ่าน read ซึ่งเราสามารถสรุป flow มันจะได้เป็น read-eval-print loop[<a href="http://www.joyofclojure.com/">7</a>](ย่อว่า REPL) [<a href="https://clojure.org/reference/reader">14</a>]</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FMC3mkLgtmX0RFp8qYVduw.png" /></figure><p>สำหรับขั้นตอน read จริงๆตรงนี้ก็ทำงานเหมือน parser คือรับ text แล้วแปลงเป็น <strong>data structure</strong> [<a href="https://clojure.org/reference/reader">14</a>] โดย <strong>data structure </strong>มีดังนี้</p><ul><li>symbols: คืออะไรก็ตามที่ไม่ได้ขึ้นต้นด้วยตัวเลข แต่สามารถมีตัวเลขตามมาได้รวมทั้งพวกสัญลักษณ์ เช่น me-and-you, I!007, hello❤, etc. จะว่าไปจะเรียกมันว่าตัวแปรก็ได้</li><li>list (), vector [], map {}, set #{}</li><li>literals: เช่น string(พวกที่อยู่ใน double quote เปิดปิด), character (พวกที่อยู่หลัง \ เช่น \n), number, nil, boolean, key( :key , :word ), etc.</li></ul><p>สำหรับ literals และพวก vector, map, set พอ eval พวกนี้จะคืนค่าเดิมออกมา ในกรณีของ list พอ eval แล้วจะเรียกฟังก์ชันหรือทำงานตามแต่ที่สมาชิกตัวแรกของมันจะสั่งให้ทำ เช่น (+ 1 2) , (defn x [] 3) ส่วน symbol จะถูกเก็บไว้เป็นตัวแปรไว้รอเรียก [<a href="https://clojure.org/reference/reader">14</a>][<a href="http://clojure-doc.org/articles/language/macros">16</a>][<a href="https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles">17</a>]</p><p>เช่น</p><pre>user=&gt; (+ 3 4)<br>7</pre><p>การทำงานจะเริ่มจาก (read)(+ 3 4) จากนั้น form ที่ส่งเข้ามานี้ (หมายถึง (+ 3 4) ) จะถูกแปลงความหมายจากก้อน text เป็น data structure ที่พร้อมถูก eval นั่นก็คือมันกลายเป็น list ที่เริ่มต้นด้วยฟังก์ชัน + ที่รับ parameter เป็น 3 4 ตามลำดับ ซึ่ง eval แล้วได้ค่า 7 นั่นเอง [<a href="http://www.joyofclojure.com/">7</a>][<a href="https://en.wikibooks.org/wiki/Learning_Clojure/Special_Forms">15</a>]</p><p>แต่เดี๋ยวก่อน นั่นไม่ได้หมายความว่าไอ่ตัว +เนี่ย พอ readแล้วมันก็ถูกแปลงเป็น symbol ด้วยหรือเปล่า? ใช่แล้ว มันคือ symbol ซึ่งเราเรียกว่า <em>verb </em>เพราะมันเป็น symbol ที่ออก action กับอะไรสักอย่าง ซึ่งตามที่กล่าวไว้ข้างบนว่า symbol มันจะ store เก็บไว้ให้รอเรียกถ้ามันมีให้เรียก [<a href="https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles">17</a>]</p><pre>user=&gt; +<br><em>#object[clojure.core$_PLUS_ 0x33d6a662 &quot;clojure.core$_PLUS_@33d6a662&quot;]</em></pre><p>นั่นหมายความว่า ตอนที่มันกำลัง eval มันเข้าไปดูว่า symbol + มัน store อะไรไว้ ซึ่งนั่นก็คือ function [<a href="https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles">17</a>] ฉะนั้นแล้ว</p><pre>user=&gt; (+ 1 (+ 1 2))<br>4</pre><p>ก็เหมือนกับว่า เมื่อ clojure eval มันจะเข้าไปดูความหมายที่เก็บไว้ของ + ก่อน</p><pre>(#object[clojure.core$_PLUS_ 0x33d6a662 &quot;clojure.core$_PLUS_@33d6a662&quot;] <strong>1</strong> (#object[clojure.core$_PLUS_ 0x33d6a662 &quot;clojure.core$_PLUS_@33d6a662&quot;] <strong>1 2</strong>))</pre><p>จากนั้นมันถึงทำการ eval แล้วได้ค่าออกมา [<a href="https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles">17</a>]</p><p>กลับเข้ามาที่ชื่อ special form ซึ่งจริงๆแล้วที่มันพิเศษกว่าชาวบ้านหรือถูกเรียกว่า special ก็เพราะว่ามันเป็น forms ที่ถูก eval พิเศษ</p><p>โดยปกติทั่วไป form จะทำงานในรูปแบบ eval (operator […operands]) หรือก็คือเราจะเอา operator ไปทำงาน กับ operands เช่นเมื่อเราบอกว่า (+ 1 2 3 4) นั่นหมายถึงว่าเราเอา + ไปทำงานกับ 1 2 3 4 [<a href="https://mitpress.mit.edu/sicp/full-text/book/book.html">20</a>]</p><blockquote><strong><em>note:</em></strong> เราเอาฟังก์ชัน + ไป apply กับ 1 2 3 4 ฉะนั้นฟังก์ชันที่ชื่อ apply ทำงานแบบนี้ (apply + [1 2 3 4]) จะเท่ากับ (+ 1 2 3 4) ก็คือเรานำ + ไปใช้ใน vector [1 2 3 4], ถ้าเล่น clojure ไปสักพักจะเจอว่ามันมีฟังก์ชันชื่อ reduce ที่ทำงานคล้ายๆกัน แต่ว่าจริงๆแล้วทำงานต่างกัน (reduce + [1 2 3 4]) จะได้เป็น (+ (+ (+ 1 2) 3) 4)</blockquote><p>แต่ทีนี้ถ้ามีกฎแค่นี้เราก็จะเกิดปัญหา นั่นก็คือ แล้วเราจะสร้างตัวแปรขึ้นมาใช้ยังไง? สมมุติเราบอกว่า (def x 3) นั่นมันหมายความเราเอา def ไป apply กับ x และ 3 ? แบบนี้ก็ดูจะไม่มีความหมาย เพราะ def ไม่ได้ยุ่ง x แบบเดียวกับที่ยุ่งกับ 3 (อย่าลืมว่าตอนที่เรา (+ 1 2 3 4) นั่นเราเอา + ไปยุ่งกับทุกตัวในลักษณะเดียวกัน)</p><p>ในทางตรงกันข้าม มันเป็นการที่เราเอา x ไปยุ่งกับ 3 เองมากกว่า แบบนี้ก็จะหลุดกฎซึ่งเป็นที่มาของ special form นั่นเอง เมื่อเราต้องการสร้างกฎใหม่ที่ไม่ได้เอา operator ไปทำงานกับ operands (แบบที่เราเอา + ไปทำงานกับ 1 2 3 4) ก็จะถูกเรียกเป็น special forms หมด ทีนี้แสดงว่าอย่าง (if &lt;เงื่อนไข&gt; &lt;ผลที่ตามมา&gt; ​&lt;ผลอื่นๆ&gt;), ex. (if (&gt; 2 1) &quot;always true, right?&quot; &quot;no ways!&quot;)ก็แสดงว่าเป็น special forms ด้วย? ใช่แล้ว แสดงว่าคุณเข้าใจ special forms มาถูกทาง เพราะเราก็ไม่ได้เอา if ไป apply หมู่กับ arguments แต่เห็นได้ชัดว่ามันมีกฎพิเศษคอยคุมอยู่ [<a href="https://mitpress.mit.edu/sicp/full-text/book/book.html">20</a>]</p><p>เรากลับมาดูตัวที่น่าสนใจซึ่งคงผ่านตามาแล้วอย่าง def สำหรับการเอาไว้ประกาศนุ่นนี่ ซึ่งdef จริงๆแล้ว def ประหนึ่ง atom สำหรับ forms เพราะเดี๋ยวเราจะได้เห็นกันว่า forms ทุกอย่างล้วนงอกมาจาก def ทั้งนั้น (ยกเว้น special forms ) แต่ก่อนอื่นเรามาดูกันว่าจริงๆแล้ว def นั้นทำอะไรกันแน่</p><pre>user=&gt; (doc def)<br>-------------------------<br>def<br>  (def symbol doc-string? init?)<br><strong>Special Form</strong><br>  Creates and interns a global var with the name<br>  of symbol in the current namespace (*ns*) or locates such a var if<br>  it already exists.  If init is supplied, it is evaluated, and the<br>  root binding of the var is set to the resulting value.  If init is<br>  not supplied, the root binding of the var is unaffected.<br><br>  Please see http://clojure.org/special_forms#def<br>nil</pre><p>ใน doc บอกไว้ว่า def นั้น</p><pre>Creates and interns a <strong>global var</strong> with the name of <strong>symbol</strong> in the current <strong>namespace</strong> (*ns*) or locates such a var if it already exists.</pre><p>อย่างที่บอกคือถ้าไม่มี def เราก็จะประกาศตัวแปรไม่ได้ และถ้าไม่มี fn เราก็จะประกาศฟังก์ชันไม่ได้ นั่นหมายความว่าเราไม่สามารถประกาศ <strong>macro</strong> ได้อีกด้วย อาจมีคนไม่เชื่อ ให้ลองใช้ sourceที่ปกติเอาไว้ดู source code ดู ลอง(source defmacro) ดู แล้วจะเห็นว่ามันสร้างมาจาก def ส่วน special form บางตัวจะใช้ defmacroสร้างขึ้น เช่น let และเอ่อ fn</p><p>(ข้างล่างขออนุญาตตัดบางส่วนทิ้ง เพราะเดี๋ยวมันจะรกเกินไป)</p><pre>user=&gt; (source defmacro)<br>(<strong>def</strong><br><br> ^{:doc &quot;Like defn, but the resulting function name is declared as a<br>....</pre><pre>user=&gt; (source fn)<br>(<strong>defmacro</strong> fn<br>  &quot;params =&gt; positional-params* , or positional-params* &amp; next-param<br>...</pre><pre>user=&gt; (source def)<br>Source not found<br>nil</pre><p>โอเค เราจะเห็นว่า def — สร้าง — &gt; defmacro — สร้าง — &gt; fn</p><p>ยังไม่สาแก่ใจตามไปดูใน source code ที่ <a href="https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj">core.clj</a> ซะหน่อย</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/649/1*UquPZdKsIkbjqDQoEif-4w.png" /></figure><p>ถึงตรงนี้อาจมีบางคนงอกคำถามใหม่มาแทน แล้ว def มันคืออะไร ทำไมเราส่อง source code มันไม่ได้ ในขณะที่มันสร้างชาวบ้านทุกๆตัว ?</p><p>งั้นลองเข้าไปส่องใน <a href="https://clojuredocs.org/clojure.core/def">clojuredocs</a> ก่อนละกัน</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/797/1*p-2QnAHlFxXxYtO2Otq0YQ.png" /></figure><p>อ่า….นั่นไงลิงค์เข้าไปดู source / คลิกพลัน</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/856/1*7rR9SUkm9JXK2v3ofGvoAQ.png" /></figure><p>(… )</p><p>ที่เราหาไม่เจอจากใน source เพราะว่ามันไม่ได้ถูก implement จาก clojure แต่มันจะถูก binding ใน compiler แปลงเป็น bytecode อีกที จะว่าไปมันก็เมคเซนส์ เพราะเราจะเอาอะไรไปสร้าง def ตั้งแต่แรกล่ะ ในเมื่อมันอยู่นอกกฎ!?</p><p>เราสามารถเข้าไปส่องได้ที่ <a href="https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java">compiler.java</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*Pk8baz8J1ImGg5HNKlJbOg.png" /><figcaption>compiler.java, บรรทัดที่ 38–45</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/542/1*ovZ7ovfuwSGhtBLxr64G7w.png" /><figcaption>compiler.java, บรรทัดที่ 105–111</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/459/1*evP3RvgD0ownSbpzhOGH_w.png" /><figcaption>compiler.java, บรรทัดที่ 407–414</figcaption></figure><p>เราจะเห็นไอเดียคร่าวๆว่ามันเอา def ไปโยงกับ parser ของ DefExpr นอกจากนั้นตัว symbol อื่นที่เห็นในไฟล์นี้ ก็ถือว่าเป็นพวก special form ทั้งนั้น ตอนนี้เราอาจกล่าวได้ว่า special form ก็คือ symbol พิเศษที่ตั้ง rule ขึ้นมาโดยเฉพาะ</p><p>ฉะนั้นเราอาจกล่าวได้ว่า special forms นั้นคือสิ่งที่ primitive ที่สุดใน clojure และเป็น ground ในการสร้างสิ่งอื่นๆทั้งหมดในโลก clojure เลยก็ว่าได้</p><h4><strong>Function</strong></h4><p>อย่างที่เราเห็นมาก่อนหน้านี้ function คือ symbol หนึ่งๆที่ทำให้เกิดเป็น forms ขึ้นมา function มีทั้งที่ build-in เข้ามาอย่างพวกตัวดำเนินการทางคณิตศาสตร์อย่าง +,-,*,/ แต่จริงๆแล้วมันคืออะไรกันแน่? เวลาเรียกใน repl มันคืนอะไรออกมา ? เกิดอะไรขึ้นเมื่อเราสร้างฟังก์ชันใหม่ขึ้นมา? แล้วอะไรคือ <em>verb</em>?</p><pre>user=&gt; +<br>#object[clojure.core$_PLUS_ 0x378b64f7 &quot;clojure.core$_PLUS_@378b64f7&quot;]</pre><pre>user=&gt; juxt<br>#object[clojure.core$juxt 0x2691d52a &quot;clojure.core$juxt@2691d52a&quot;]</pre><pre>user=&gt; (defn lol [lol-what] (str &quot;hahaha &quot; lol-what))<br>#&#39;user/lol<br>user=&gt; lol<br>#object[user$lol 0x2948ef0a &quot;user$lol@2948ef0a&quot;]</pre><p>function โดยพื้นฐานแล้วจะรับค่าอะไรสักอย่างแล้วก็จะคืนค่าอะไรสักอย่าง</p><pre>user=&gt; (fn [] 10)<br>#object[user$eval1275$fn__1276 0x7b781f24 &quot;user$eval1275$fn__1276@7b781f24&quot;]</pre><pre>user=&gt; ((fn [] 10))<br>10</pre><pre>user=&gt; ((fn [x] (+ 10 x)) 5)<br>15</pre><p>การที่จะทำให้ function ทำงานได้มันจะต้องอยู่ในรูป forms ฉะนั้น เราจึงเห็นว่าถ้าเราใส่วงเล็บมันสองชั้น มันจะอยู่ในรูปฟอร์มที่ไม่ต้องการรับ argument ซึ่งทำให้มัน eval ได้ค่าออกมาเลยได้ ส่วนอันแรกมันก็คืนค่าออกมาเป็น <em>verb </em>อันที่สามคือเราเพิ่ม argument มันเข้าไปด้วย แล้วใส่ 5 เข้าไปในฟังก์ชันนั้น[<a href="http://www.joyofclojure.com/">7</a>][<a href="https://aphyr.com/posts/303-clojure-from-the-ground-up-functions">13</a>] ทีนี้เราก็จะพอบอกได้ว่า function โดยการทำงานมันเหมือนเราสร้างอะไรสักอย่างทิ้งไว้ ซึ่งถ้าไม่มีใครเรียกมันก็จะหายไป แต่ถ้ามีการเรียกการทำงานมันก็จะคืนค่าออกมา ลองดูที่ตัวอย่างสุดท้าย เราจะเห็นว่า จริงๆแล้วมันก็คือฟังก์ชันที่เอาค่าที่รับเข้ามาไป + 10 แล้วคืนค่ากลับ แต่ x ที่เป็น argument ตรงนี้เราเรียกว่ามัน unbound เพราะมันยังไม่มีค่าในตัวมัน …มันกำลังรอค่าอะไรสักอย่างมาแทนที่มันอยู่</p><pre>user=&gt; (let [x 1] (+ x 1))<br>2<br>user=&gt; (let [x 2, y 3] (+ x y))<br>5</pre><p>จากตัวอย่างการใช้ let เราจะเห็นว่าอย่าง (+ x 1) จริงๆก็คือฟังก์ชันที่เอาไปบวก 1 แล้วคืนค่านั้นออกมา แต่ทีนี้ x ในนี้ก็ยังเป็น unbound อยู่จนกระทั่งมี let ที่เอาค่า 1 ไปใส่ให้ x แล้วทำให้เราได้ (+ 1 1) ซึ่งคืนค่าออกมาเป็น 2 ทีนี้กลับมาดูฟังก์ชัน</p><pre>user=&gt; ((fn [x] (+ 10 x)) 5)<br>15</pre><p>เราอาจบอกได้ว่า (fn [x] (+ 10 x)) นั้นถ้าเราตั้งชื่อสมมุติเป็น add-ten มันจะได้ออกมาเป็น (add-ten 5) ซึ่งก็จะทำในลักษณะเดียวกับ let คือนำ argument ที่ได้รับไป bound เข้ากับ unbound ตามตำแหน่งของ argument [<a href="https://aphyr.com/posts/303-clojure-from-the-ground-up-functions">13</a>] ถ้ายังจำกันได้ def ก็ทำอะไรที่คล้ายๆกันกับ letเหมือนกัน แต่ต่างกันที่ scope โดย def จะไป bind ในระดับ global ในขณะที่ let จะ bind แค่ในระดับวงเล็บ</p><p>ทีนี้เรากลับมาดูที่ไอ่ก้อนแรก</p><pre>#object[<strong>user</strong>$eval1275$<strong>fn__1276</strong> <strong>0x7b781f24</strong> “user$eval1275$fn__1276@7b781f24”]</pre><p>ส่วนนี้มันคือ instance เพราะถ้ายังจำกันได้ตั้งแต่ตอนต้นว่า function ใน clojure สุดท้ายจะโดน compile ไปเป็น class</p><p>เรามาดู anatomy ของ instance อันนี้ดีกว่า เริ่มจาก function + เราเห็นว่า #object[<strong>clojure.core</strong>$<strong>_PLUS_</strong> <strong>0x378b64f7</strong> “clojure.core$_PLUS_@378b64f7”] เริ่มจาก clojure.core ตรงนี้มันคือ <em>namespace</em><strong> </strong>และ _PLUS_ (ซึ่งอันที่จริงมันแทนสัญลักษณ์ + )คื่อชื่อของ function ส่วน 0x378b64f7 นี่คือ memory address [<a href="https://aphyr.com/posts/303-clojure-from-the-ground-up-functions">13</a>]</p><p>อะไรคือ <em>namespace </em>ของ<em> </em>clojure ถ้าเอาอย่างง่ายเลยมันคือที่อยู่ที่ฟังก์ชันนั้นเขียนขึ้นมา เช่นในที่นี้ก็คือใน core</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/499/1*QRJytd-7KTs4K_UbQYmStg.png" /><figcaption>ตามไปดู clojure.core</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/414/1*Z_gcDC2nXOZZCdSD3wJhfA.png" /><figcaption>(ns clojure.core)</figcaption></figure><p>ซึ่งจริงๆแล้วตัวมาโคร ns นี่เองที่เป็นตัวสร้าง namespace ของไฟล์นั้นๆ [<a href="http://www.joyofclojure.com/">7</a>]</p><p>ตัวอย่างเพิ่มเติม เช่น</p><pre>lein new app-to-show<br>cd app-to-show<br></pre><p>แล้วเราจะได้ folder structure ดังนี้</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/208/1*ou5rMVRXLgfzPmQSxzWfFQ.png" /></figure><p>เข้าไปดูไฟล์ core.clj</p><pre>(ns app-to-show.core)</pre><pre>(defn foo<br>  &quot;I don&#39;t do a whole lot.&quot;<br>  [x]<br>  (println x &quot;Hello, World!&quot;))</pre><p>โอเค ดูข้างบนสุด เราจะได้ <em>namespace </em>สำหรับไฟล์ core.clj ซึ่งมันอยู่ใน <em>namespace</em> app-to-show.core แล้วถ้าเราจะพูดถึงฟังก์ชัน foo มันก็คือ app-to-show.core$foo โดยปกติ clojure มักจะนิยมตั้ง namespace ตาม folder structure</p><p><strong>function โดยง่ายสุดประกาศผ่าน </strong><strong>defn</strong></p><pre>user=&gt; (defn plus-ten [x] (+ 10 x))<br>#&#39;user/plus-ten<br>user=&gt; (plus-ten 10)<br>20</pre><p>โครงสร้างของการประกาศผ่าน defn มีดังนี้</p><pre>(defn &lt;function-name&gt; &lt;vector-of-parameters&gt; &lt;body&gt;)</pre><p>นั่นก็คือเริ่มจาก defn ตามด้วยชื่อ function function-name แล้วตามด้วย <em>vector</em> ที่ข้างในรับ parameter เช่น[x y z] แล้วตามด้วย body เช่น (+ x y z) ประกอบกันจะได้ (defn function-name [x y z] (+ x y z))</p><p>(ถ้าไม่ใส่วงเล็บตรง body อย่าง (defn function-name [x y z] + x y z) มันจะคืนเป็นค่าของ z มา นั่นก็เพราะว่าตามปกติธรรมชาติของ clojure มันมักจะคืนตัวหลังสุดก่อน)[<a href="https://aphyr.com/posts/303-clojure-from-the-ground-up-functions">13</a>]</p><p>แต่ทีนี้ฟังก์ชันมันไม่จำเป็นต้องรับ argument ก็ได้ จะได้เป็น</p><pre>user=&gt; (defn return-5 [] 5)<br>#&#39;user/return-5<br>user=&gt; (return-5)<br>5</pre><p><strong>Macro </strong>นั้นหน้าตาคล้ายฟังก์ชันมาก เราลองมาดูตัวอย่างการประกาศ macro กันก่อน</p><pre>(defmacro t1 [x] x)</pre><p>อ้าวหน้าตามันก็เหมือนฟังก์ชันเลยนี่หว่า คือมีชื่อ คือ t1 รับค่าคือ x แล้วมี body ในที่นี้คือคืนค่า x เอ๊ะแบบนี้มันก็ไม่ต่างนี่หว่า จริงๆแล้วมันอาจจะต่างนิดหน่อยในตัวอย่างนี้ ไม่เชื่อลอง (defn t2 [x] x) จากนั้นพิมพ์ t2 ลงไปบน repl</p><pre>#object[user$t2 0x712b623d &quot;user$t2@712b623d&quot;]</pre><p>ทีนี้ลองจัด t1 กันบ้าง จะเจอกับ error หน้าตาคล้ายๆแบบนี้</p><pre>CompilerException java.lang.RuntimeException: Can&#39;t take value of a <strong>macro</strong>: #&#39;user/t2, compiling (/private/var/folders/db/vnkkt7mx5wj84jxcwh4jplsw0000gn/T/form-init1239338506849356429.clj:1:1062)</pre><p>กลับมาที่ macro มีคนบอกว่ามันต่างจากฟังก์ชันตรงที่</p><ul><li>macro แปลงโค้ดๆนึงไปเป็นโค้ดอีกแบบนึง</li><li>function แปลงค่านึงไปเป็นอีกค่านึง</li></ul><p>โอ้ว นี่มัน abstract ระดับนึงเลยนะเนี่ย [<a href="http://blog.klipse.tech/clojure/2016/05/01/macro-tutorial-1.html">9</a>] อันที่จริงเรามาทำ”อะไรๆกับโค้ด” อย่างที่เขาบอกดูบ้าง อย่าง clojure จะใช้ funtion ขึ้นก่อนตามด้วย argument เช่น ‘+’ จริงๆมันก็คือฟังก์ชัน add เช่น (+ 1 1)จะได้ 2 ทีนี้เราอยากลองให้มันสลับที่ของ argument บ้าง เช่น แทนที่ (/ 20 2) จะได้ 10 มันจะต้องสลับเป็น (/ 2 20) แล้วได้ 1/10 แทน โดยเราจะลองทั้ง macro แล้วก็ function เขียนเหมือนๆกัน โดยเราจะใช้ฟังก์ชัน list เข้ามาช่วยสร้าง list อีกที</p><pre>user=&gt; (defmacro m [x<strong> y z</strong>] (list x <strong>z y</strong>))<br>user=&gt; (defn f [x <strong>y z</strong>] (list x <strong>z y</strong>))</pre><pre>user=&gt; (m / 20 2) <br>1/10<br>user=&gt; (f / 20 2) <br>(#object[clojure.core$_SLASH_ 0x36e8bb94 &quot;clojure.core$_SLASH_@36e8bb94&quot;] 2 20)</pre><pre>(defn f [x y z] (x y z)) ;; แก้เกมสำหรับฟังก์ชัน เอาใหม่ๆ<br>user=&gt; (f / 20 2)<br>1/10</pre><p>อืม มาดูที่ฟังก์ชัน f ก่อน ถ้าเราดูดีๆเราจะพบว่าคำตอบมันสลับที่กันอยู่จริง คือ 2 สลับกับ 20 ส่วนไอ่ก้อนๆข้างหน้าจริงๆแล้วมันคือ / นั่นแหละ (ดูที่คำว่า _<strong>SLASH_</strong>) แสดงว่า / มันไม่ได้ทำงานรับ 2 กับ 20 แล้ว resolve แต่อย่างใด ซึ่งจริงๆก็ถูกต้องแล้วเพราะเราใช้ฟังก์ชัน list มันก็ควรจะคืนค่ามาเป็น list ถูกไหม ได้เป็น (/ y z) ซึ่ง / มันก็คืนค่ามาเป็น <em>verb</em> ที่ /ชี้ไปหานั่นเอง</p><p>ในขณะที่มาโคร m มันคืนค่าออกมาเป็น 1/10 ซึ่งมันหมายความว่าจริงๆแล้ว มันมองเห็นว่า (/ y z) เป็น <strong>code ไม่ใช่ list</strong>! จากนั้นมันจึงทำการ eval ทั้งก้อนนี้อีกที [<a href="http://www.joyofclojure.com">7</a>][<a href="https://www.braveclojure.com/writing-macros/">10</a>]</p><blockquote>ส่วนของ macro ค่อนข้างซับซ้อนและน่าจะมีอะไรให้พูดอีกเยอะมากๆ คร่าวๆไว้แค่นี้ก่อนฮะไว้มาเขียนต่อในบทความอื่นๆ</blockquote><h4>บทสรุป การสร้างจักรวาล Clojure</h4><ol><li>Compiler และ Reader สำหรับ eval และ read เพื่อแปลง Clojure ไปเป็น JAVA bytecode และนำไปทำงานใน JVM</li><li>Reader forms เปรียบเสมือน atom ของโลก Clojure พวกนั้นได้แก่ symbol, literals, list, vector, map, set</li><li>Symbol บางตัว สมควรที่จะมีความพิเศษเหนือชาวบ้าน หรือมีกฎที่แตกต่างออกไป เช่นเอาไปสร้างเงื่อนไข สร้าง function พวก symbol พิเศษนี้เราเรียกว่า special forms</li><li>ในโลก Clojure ตัว verb ที่สามารถทำงานได้จะอยู่ในรูป list เราเรียกพวกนี้ว่า forms ซึ่งประกอบไปด้วย function, macro, และ special forms</li></ol><h4>Acknowledgement:</h4><p>ขอบคุณพี่ๆเพื่อนๆที่ตรวจทานบทความ ให้คำแนะนำ และเสนอแนะ <a href="https://medium.com/u/7ee6829f80e3">I&#39;Boss Potiwarakorn</a>, <a href="https://medium.com/u/1581768276dc">Weerasak Chongnguluam</a>, <a href="https://medium.com/u/204a8f53be22">Veha Suwatphisankij</a>, <a href="https://medium.com/u/69eef96aba37">Thanat Jatuphattharachat</a>, <a href="https://medium.com/u/56a18f6da0bd">Tap</a></p><h4>References:</h4><p>[1] <a href="https://insights.stackoverflow.com/survey/2017">https://insights.stackoverflow.com/survey/2017</a></p><p>[2] <a href="https://clojure.org/">https://clojure.org/</a></p><p>[3] <a href="http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/">http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/</a></p><p>[4] <a href="https://www.braveclojure.com/getting-started/">https://www.braveclojure.com/getting-started/</a></p><p>[5] <a href="https://en.wikipedia.org/wiki/List_of_JVM_languages">https://en.wikipedia.org/wiki/List_of_JVM_languages</a></p><p>[6] <a href="http://blog.ndk.io/clojure-compilation.html">http://blog.ndk.io/clojure-compilation.html</a></p><p>[7] Michael Fogus and Chris Houser. 2014. <em>The Joy of Clojure</em>(2nd ed.). Manning Publications Co., Greenwich, CT, USA.</p><p>[8] <a href="https://clojure.org/reference/java_interop#typehints">https://clojure.org/reference/java_interop#typehints</a></p><p>[9] <a href="http://blog.klipse.tech/clojure/2016/05/01/macro-tutorial-1.html">http://blog.klipse.tech/clojure/2016/05/01/macro-tutorial-1.html</a></p><p>[10] <a href="https://www.braveclojure.com/writing-macros/">https://www.braveclojure.com/writing-macros/</a></p><p>[11] <a href="https://www.safaribooksonline.com/library/view/clojure-programming/9781449310387/ch09s05.html">https://www.safaribooksonline.com/library/view/clojure-programming/9781449310387/ch09s05.html</a></p><p>[12] <a href="http://clojure-doc.org/articles/language/functions.html">http://clojure-doc.org/articles/language/functions.html</a></p><p>[13] <a href="https://aphyr.com/posts/303-clojure-from-the-ground-up-functions">https://aphyr.com/posts/303-clojure-from-the-ground-up-functions</a></p><p>[14] <a href="https://clojure.org/reference/reader">https://clojure.org/reference/reader</a></p><p>[15] <a href="https://en.wikibooks.org/wiki/Learning_Clojure/Special_Forms">https://en.wikibooks.org/wiki/Learning_Clojure/Special_Forms</a></p><p>[16] <a href="http://clojure-doc.org/articles/language/macros">http://clojure-doc.org/articles/language/macros</a></p><p>[17] <a href="https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles">https://aphyr.com/posts/301-clojure-from-the-ground-up-first-principles</a></p><p>[18] <a href="https://en.wikipedia.org/wiki/Interpreted_language">https://en.wikipedia.org/wiki/Interpreted_language</a></p><p>[19] <a href="https://www.destroyallsoftware.com/compendium/types?share_key=baf6b67369843fa2">https://www.destroyallsoftware.com/compendium/types?share_key=baf6b67369843fa2</a></p><p>[20]Harold Abelson and Gerald Jay Sussman with Julie Sussman. Structure and Interpretation of Computer Programs (2nd edition). The MIT Press.</p><p>[21]<a href="https://insights.stackoverflow.com/survey/2018/">https://insights.stackoverflow.com/survey/2018/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a577f575932a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[อะไรคือ universal racism กันแน่?]]></title>
            <link>https://medium.com/@bullrish/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-universal-racism-%E0%B8%81%E0%B8%B1%E0%B8%99%E0%B9%81%E0%B8%99%E0%B9%88-ca5223a027e0?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/ca5223a027e0</guid>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Fri, 24 Mar 2017 20:54:34 GMT</pubDate>
            <atom:updated>2017-03-25T03:55:44.783Z</atom:updated>
            <content:encoded><![CDATA[<p>อะไรคือ universal racism กันแน่?</p><p>1. นั่งคิดว่ามันคืออะไร เขาจะหมายถึงการเหยียดที่กระทำเป็น uniform distribution แบบนั้นเหรอ หมายถึงเหยียดทุกกลุ่มทุกคนอย่างเท่าๆกันด้วยการกระจายเท่าๆกัน (หมายความว่าไม่ได้มีกลุ่มใดกลุ่มนึงโดนเหยียดเป็นพิเศษ) แต่ถ้าแบบนี้มันจะตอบยากเป็นพิเศษ เพราะเราไม่รู้ว่ามันต้องกระจายไปถึงเท่าไหร่หรือเปล่า? โอเคลองนึกภาพตาม สมมุติว่าเรามีเซตของกลุ่มที่จะต้องเหยียดเป็น {a, b, c, …, n} เมื่อ n คือกลุ่มที่จะเหยียดอันสุดท้าย ถ้างั้นหมายความว่าเราจำเป็นที่จะต้องเหยียด n กลุ่ม (และแน่นอนด้วยการกระจายแบบเท่าๆกัน — uniform distribution) คำถามคือ เราแน่ใจได้อย่างไรว่าเรา reach ไปถึง n กลุ่มแล้ว ? (แต่แน่นอนเพื่อความ ideal เราจะมาพูดถึงเรื่องนี้อีกรอบในภายหลัง) หรือเอาเข้าจริงคือ เราจะมั่นใจได้อย่างไรว่าเราจะเหยียดได้แบบ universal จริงๆ</p><p>2. ต่อมาเรามั่นใจได้อย่างไรว่าจะไม่มีใครถูกเหยียดเป็นพิเศษ? หมายถึงว่าจากข้อ 1. สมมุติว่า ในเมื่อเราไม่สามารถ reach ไปถึง n ได้เนื่องด้วยความไม่รู้ (ขีดจำกัดว่าด้วยความไม่รู้ว่ามีกลุ่มที่ตกหล่น) แสดงว่าถ้ามีการตกหล่น มันจะต้องมีกลุ่มใดกลุ่มนึงที่โดนเหยียดเป็นพิเศษใช่หรือไม่ (ต้องใช่สิ) หรือถ้าพูดกันแบบ real world problem เราจะแน่ใจได้อย่างไรว่าถ้าเกิดคนถือ universal racism หรือเหยียดชาวบ้านอย่างเท่าๆกันแบบเต็มไปหมดแล้ว (หมายถึงแนวคิดนี้แพร่เป็นวงกว้างจนแทบจะ dominating สังคมแล้ว) จะไม่มีกลุ่มใดกลุ่มนึงที่โดนเหยียดเป็นพิเศษ และไม่มีสภาวะโครงสร้างใดๆที่เอื้อให้มีคนถูกเหยียดเป็นพิเศษ? และเราจะแน่ใจได้อย่างไรว่าแนวคิดนี้ไม่ได้สร้างปัญหาการกดทับที่รุนแรงยิ่งกว่าเดิมมหาศาล เนื่องด้วยมันทำให้ฝั่งที่มีพลวัตอำนาจแต่เดิมแล้วคุมเกมได้ยิ่งกว่าเดิม? (หมายความว่าหากประชากรส่วนใหญ่ไม่มีทางจะเหยียดแบบ reach ไปได้ n/2 ด้วยซ้ำ การเหยียดกลุ่ม a ไปถึงกลุ่ม n/2 จะยิ่งกว่าเป็นการเหยียดซ้ำไปซ้ำมาอย่างซ้ำซาก)</p><p>3. นั่นนำมาสู่ข้อเสนอจากข้าพเจ้าถึงการ จะไปสู่ universal racism ได้อย่างไร? ข้าพเจ้าขอเสนอว่า</p><blockquote><em>3.1 ตกลงให้แบ่งกลุ่มจัด stereotype ให้ชัดเจนว่าจะมีจำนวน n กลุ่มขั้นต่ำที่จะต้องเหยียดให้ครบ</em></blockquote><blockquote><em>3.2 ให้กลุ่ม A เหยียดทุกกลุ่มในวัน α จากนั้นวันต่อมาให้กลุ่ม Β เหยียดทุกกลุ่มในวัน β ไปเรื่อยๆ…จนถึงกลุ่ม n ในวันที่ N จากนั้นก็วนเริ่มใหม่</em></blockquote><p>หรือเขาจะหมายถึงแบบอื่นกัน ?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca5223a027e0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[note to myself about how to set up software project]]></title>
            <link>https://medium.com/@bullrish/note-to-myself-about-how-to-set-up-software-project-84a30e432a77?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/84a30e432a77</guid>
            <category><![CDATA[full-stack-development]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Thu, 23 Jun 2016 17:19:42 GMT</pubDate>
            <atom:updated>2016-06-23T17:19:42.915Z</atom:updated>
            <content:encoded><![CDATA[<p>1.convention สำคัญ โดยเฉพาะอย่าง project ที่มีคน contribute หลายคน แต่เวลาเราพูดถึง convention บางทีการที่ dev ไม่ได้ทำตาม convention มันอาจจะมาจากตัว convention เองที่ไม่ชัดเจน หรือเป็น case ก้ำๆกึ่งๆ อันนี้ก็ต้องคุยกัน และพยายามปรับปรุงมันไปเรื่อยๆ (คือแม้แต่เรื่องที่ดูเหมือนจะ strict และ static มากๆ เราก็ไม่ควรให้มันอยู่นิ่ง เราควรทำให้มัน better ฉะนั้น convention จึงสมควรถูกพิจารณาว่าเป็นอะไรที่ dynamic ตั้งแต่แรก)</p><p>2.structure และ architecture ของตัว project สำคัญตั้งแต่แรกเริ่ม การวางแย่มาตั้งแต่ตอนแรกจะทำให้ตอนหลังมีปัญหาที่จะต้องรื้อตัว proj นั้นใหม่ แต่กระนั้นก็ไม่มีอะไรที่ดีตั้งแต่แรกหรือเราก็ไม่รู้ว่าตอนแรกที่คิดว่าดีจะกลายเป็นแย่ตอนท้ายหรือเปล่า แต่พิจารณาง่ายๆว่าตัวโครงสร้างมันเป็น modular พอหรือเปล่า มัน independent ขนาดไหน ก็คือควรพยายามทำให้มันเป็นตัวต่อ lego ถอดๆเสียบๆได้ ยิ่งในยุคที่เทคโนโลยีไปกันไวมาก การวางโครงสร้างไว้ให้ดีจึงเป็นอะไรที่สำคัญมาก นอกจากนั้นยังทำให้ง่ายต่อ developer ต่อการทำความเข้าใจภาพรวมของตัว software นั้นๆด้วย</p><p>3.naming ขอพูดเรื่องนี้แยกจาก convention เรื่องนี้ดูเหมือนเล็ก แต่จริงๆแล้วใหญ่มาก การคิดระบบตั้งชื่อที่ดีตั้งแต่ตอนแรกช่วยให้เราสามารถทำงานได้เร็วขึ้นอย่างมีนัยยะสำคัญ เพราะ 1. ไม่เกิดการสับสนระหว่างชื่อนั้นนี้ และป้องกันไม่ให้เกิดการตั้งชื่อซ้ำได้ 2. ชื่อที่ดีจะทำให้เราสามารถ focus และทำให้ code มัน readability สูงขึ้น คิดง่ายๆว่าถ้าตั้งชื่อ function ที่อ่านปุ๊บรู้เลยว่ามันเอาไว้ทำอะไร มันก็เพิ่ม flow มากๆแล้ว ปัญหาคือ แล้วถ้าเกิด function มันทำงานใกล้กันมากล่ะๆ อันนี้เป็นปัญหาจริงๆ มีหลายข้อเสนอ แต่สำหรับเรา ถ้ามัน refactor ไม่ได้ หรือยุบรวม function ไม่ได้ เราคิดว่าเรายอมตั้งชื่อ function ให้ยาวดีกว่า แล้วก็ comment เพิ่มไปอีกนิดหน่อย ซึ่งแน่นอนว่าวิธีนี้ก็มี cost ของมัน</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=84a30e432a77" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting started with Hapi + Firebase + AngularJS]]></title>
            <link>https://medium.com/@bullrish/%E0%B9%81%E0%B8%95%E0%B9%88%E0%B9%80%E0%B8%94%E0%B8%B4%E0%B8%A1%E0%B8%9A%E0%B8%97%E0%B8%84%E0%B8%A7%E0%B8%B2%E0%B8%A1%E0%B8%99%E0%B8%B5%E0%B9%89%E0%B9%81%E0%B8%A2%E0%B8%81%E0%B9%80%E0%B8%9B%E0%B9%87%E0%B8%99%E0%B8%AA%E0%B8%AD%E0%B8%87%E0%B8%AA%E0%B9%88%E0%B8%A7%E0%B8%99%E0%B9%81%E0%B8%A5%E0%B8%B0-publish-%E0%B8%84%E0%B8%A3%E0%B8%B1%E0%B9%89%E0%B8%87%E0%B9%81%E0%B8%A3%E0%B8%81%E0%B9%83%E0%B8%99-https-cmmakerclub-com-c494855f4d0d?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/c494855f4d0d</guid>
            <category><![CDATA[angularjs]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[hapijs]]></category>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Fri, 20 May 2016 20:56:08 GMT</pubDate>
            <atom:updated>2016-05-22T12:03:23.773Z</atom:updated>
            <content:encoded><![CDATA[<p>แต่เดิมบทความนี้แยกเป็นสองส่วนและ publish ครั้งแรกใน <a href="https://cmmakerclub.com/">https://cmmakerclub.com/</a> เพื่อความสะดวกภายหลังจึงขอนำมาเก็บในบล็อกส่วนตัวด้วย</p><p>เนื่องจากบทความนี้ใช้เครื่องมืออยู่สามเครื่องมือ จึงขออนุญาตแบ่งบทความออกเป็นสามท่อนตามแต่ละเครื่องมือ คือ</p><ol><li><em>Hapi.js</em></li><li><em>Firebase</em></li><li><em>Angular.js</em></li></ol><h4>Motivation</h4><p>ที่มาของบทความนี้เกิดจากว่าต้องการหาตัวจัดการกับ server ที่ดูง่ายๆ และทำงานกับ database ที่ไม่ยากเกินไปนัก ในระดับที่นั่งเขียนโค้ดกันไม่เกินชั่วโมงสองชั่วโมงก็ได้คร่าวๆแล้ว ตัว framework ที่เราเลือกผลจึงตกมาที่ Hapi เพราะดูจาก document แล้วอ่านไม่ยากเกินไป รวมทั้ง API ที่ดูเข้าใจง่าย (นอกจากนั้นอีกเหตุผลคือไม่เคยลอง แล้วอยากลอง) ในขณะเดียวกัน Firebase ก็ถือว่าเป็นทางเลือกที่ดีเนื่องจากเป็น realtime database ที่อยู่บน cloud เรียกใช้งานได้ง่าย มี gui รองรับ พูดง่ายๆว่าการมี firebase ทำให้ประหยัดเวลาในการจัดการกับพวก backend ไปได้มาก (เหมาะกับพวกชอบลองเทคโนโลยีนุ่นนี่เป็น hobbyist อย่างเรา) นอกจากนั้นยังสามารถทำพวก single-page application ได้อย่างง่ายอีกด้วย(เพราะเสมือนว่ามีคนทำ backend ให้คร่าวๆแล้ว — ถ้ายังจำได้เคยมีแข่งสร้าง web app โดยกำหนดให้ทำแค่ frontend only แนวแฮคคาธอนด้วยแหละ สนับสนุนโดย firebase, ฉลาดจริงๆ) ทั้งนี้ตัว front-end ที่เราเลือกมาจับโยงข้อมูลคือ <a href="https://cmmakerclub.com/tag/angularjs/">AngularJS</a></p><p>สิ่งที่ต้องมีก่อนเล่นกับ Hapi + firebase + angularJS นั่นก็คือ <a href="https://nodejs.org/en/">Node.js</a> และ <a href="https://www.npmjs.com/">npm</a> ซึ่งพวกนี้เอาไว้เล่นกับ Hapi ส่วน firebase กับ angularJS ไม่จำเป็นต้องติดตั้งอะไร เพราะเดี๋ยวเราจะเรียกใช้ไปเลย แต่ทั้งนี้เราต้องการ node.js เวอร์ชันใหม่ๆขึ้นไป (เพื่อรองรับ <strong>ECMAScript 5</strong>)</p><h3><a href="http://hapijs.com/">1. HapiJS</a></h3><p>Hapi เป็น framework ตัวหนึ่งที่ทำงานบน Node.js โดยในที่นี้เราจะเอามาไว้สร้าง เซิฟเวอร์แล้วให้มันทำหน้าที่เป็นตัว render ไฟล์ html แบบเบื้องต้น</p><p>เปิด terminal (หรือ command prompt) cd ไปยัง path ที่ต้องการทำงาน จากนั้นพิมพ์คำสั่งลงไป</p><blockquote>npm init</blockquote><p>จากนั้นจะมีให้กรอกรายละเอียดเกี่ยวกับ project ของเรา ก็ใส่รายละเอียดลงไปแล้วกด enter ถ้าไม่ใส่มันจะเป็นค่า default ซึ่งก็คือค่าที่อยู่ในวงเล็บ</p><p>จากนั้นจะมีสรุปข้อมูลและถามว่าเราชัวร์ไหม ก็ให้ตอบ yes</p><p>เพื่อทำการติดตั้ง module ของ hapi ลงใน project ใช้คำสั่งนี้</p><blockquote>npm install hapi — save</blockquote><p>จากนั้นสร้างไฟล์ใหม่ชื่อ server.js แล้วนำโค้ดด้านล่างไปใส่ คำอธิบายการทำงานอยู่ตรงคอมเม้น ส่วน ‘use strict’ ขออธิบายคร่าวๆว่าเป็น features ใหม่ของ ECMAScript 5 ซึ่งจะทำให้มันเช็ค error มากขึ้นและป้องกันมากขึ้น<br>อ่านเพิ่มเติมได้ที่นี่ <a href="http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/">ECMAScript 5 Strict Mode</a> ซึ่งไอ่ตัวนี้มัน require พวก node เวอร์ชันใหม่ๆ ก็ทำการอัพเดทกันก่อน (อาจจะอัพเดทผ่าน nvm — node version manager, หรืออัพเดททับกันดิบๆเลยก็ได้)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/de78894d5d216725c04df0fba5cb3c0c/href">https://medium.com/media/de78894d5d216725c04df0fba5cb3c0c/href</a></iframe><p>จากนั้นกลับไปที่ terminal แล้วใช้คำสั่งนี้</p><blockquote>npm start</blockquote><p>ถ้าสำเร็จจะขึ้นเป็น</p><blockquote>SERVER STARTED: <a href="http://Wasawats-MacBook-Pro.local:2000">http://Wasawats-MacBook-Pro.local:2000</a></blockquote><p>(ซึ่งค่าตรง server.info.uri ที่เราให้แสดง log ก็ได้มาจากต้นตอของ server นั่นแหละ ซึ่งในที่นี้คือ <a href="http://Wasawats-MacBook-Pro">http://Wasawats-MacBook-Pro</a> )</p><p>นั่นมันหมายความว่าเราได้สร้าง server เบื้องต้นขึ้นมาแล้ว แต่ผมอยากกำหนดให้มันพิมพ์เป็น localhost แล้วตามด้วย port เพราะรู้สึกว่าตอนนี้มันยาวไปสิ่งที่ผมต้องทำคือเพิ่ม host ไปใน server.connection</p><pre>server.connection({<br> port: 3000,<br> host: ‘localhost’<br>});</pre><p>จากนั้นถ้าเราลอง npm start จะเห็นว่าตอนนี้แทนที่จะเป็น <strong>http://Wasa...</strong> ยาวๆ มันเปลี่ยนเป็น <strong>http://localhost:3000</strong> แทน</p><p>จากนั้นได้เวลาจัดการส่วนอื่นเพิ่มเติม อย่างการทำหน้า html</p><p>ก่อนอื่นเราต้องติดตั้ง plugin เข้าไปก่อน โดยเราจะติดตั้งตัวที่ชื่อว่า inert มาช่วยในการจัดการกับ static files</p><blockquote>npm install — save inert</blockquote><p>จากนั้นเพิ่มโค้ดนี้เข้าไปในไฟล์ server.js</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b70804f8308875d681be5741c7af077/href">https://medium.com/media/8b70804f8308875d681be5741c7af077/href</a></iframe><p>จะเห็นว่ามีการเรียกใช้ inert โดยผ่านเป็น parameter เข้าไปใน function ต่อจากนั้นถ้ามาดูตรง reply จะเห็นว่าจำเป็นต้องมี subfolder ชื่อ public และมี file คือ index.html อยู่ในนั้น จากนั้นดูตรง path จะเห็น ‘/test’ นั่นหมายความว่าเราได้สร้าง route ในที่นี้ก็คือ <a href="http://localhost:3000/test">http://localhost:3000/test</a></p><p>แน่นอนว่าเรายังไม่มี public และ index เราเลยต้องมาสร้างก่อนโดยสร้าง subfolder คือ public แล้วสร้าง index.html ลงไปในนั้น ซึ่งเบื้องต้นใน index.html ใส่โค้ดนี้ลงไป</p><pre>&lt;body&gt;<br>&lt;h1&gt; Hello world &lt;/h1&gt;<br>&lt;/body&gt;</pre><p>จากนั้น npm start แล้วตามเข้าไปดู ในที่นี้คือ <a href="http://localhost:3000/test">http://localhost:3000/test</a> ถ้าสำเร็จก็จะขึ้น Hello world โชว์หรา</p><p>ก่อนที่เราจะไปส่วนต่อไปเรามาหาวิธีโยนข้อมูลเข้า database แบบง่ายๆก่อนด้วยการให้มันโยนผ่าน jsonp ครับ</p><p><a href="https://en.wikipedia.org/wiki/JSONP">jsonp</a> ย่อมาจาก JSON with padding จากการไล่ๆดูเพื่อหาคำอธิบายที่ฟังดูง่ายๆคิดว่าอันนี้น่าจะพอใช้ได้</p><blockquote>“JSONP is script tag injection, passing the response from the server in to a user specified function” — <a href="https://remysharp.com/2007/10/08/what-is-jsonp">https://remysharp.com/2007/10/08/what-is-jsonp</a></blockquote><p>หรือก็คือมันมีการอนุญาติให้เราใส่ข้อมูลผ่าน URL แล้วยิงเข้าไปที่ DOM (ผ่านการ inject ด้วย tag &lt;script&gt;) ซึ่งอยู่ในรูปฟังก์ชันให้เรียกใช้ ถ้ายังงงๆลองดูตัวอย่าง</p><p>ถ้าสมมุติเราอยากได้ json เราก็ทำการส่ง request เข้าไป เราก็จะได้ JSON ในรูปแบบประมาณนี้</p><pre>[{<br>“id”: 1,<br>“name”: “Cameron”<br>}]</pre><p>ส่วน JSONP จะใกล้เคียงเพียงแต่โดนยัดไปอยู่ในรูปฟังก์ชันให้เรียกใช้ (โดยผ่านทาง callback)</p><pre>someCallback([{<br>“id”: 1,<br>“name”: “Cameron”<br>}]);</pre><p>ตัวอย่างการเรียกใช้ callback</p><pre>someCallback = function(data){<br>console.log(data.id); // which data.id = 1<br>};</pre><p>ทีนี้ใน Hapi มันจะมีตัวช่วยในการที่เราจะใช้งาน JSONP กับการส่งข้อมูลไปที่ firebase ผ่าน URL แต่ก่อนอื่น ลองดูตัวอย่าง code ข้างล่างนี้ก่อน</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d15c1d22f895c1dc55fa0dd49acfab62/href">https://medium.com/media/d15c1d22f895c1dc55fa0dd49acfab62/href</a></iframe><p>เรามาดูที่ server.route ก่อน จะเห็นว่ารอบนี้ตรง path ของเราเป็น</p><pre>path: ‘/{temperature}/{humidity}/’</pre><p>ทีนี้ตรง <strong>{temperature}</strong> กับ <strong>{humidity}</strong> จะเป็น key ที่เราจับคู่กับข้อมูลที่เราใส่เข้าไปใน URL เช่น สมมุติว่าผมอยากส่งค่า temperature เป็น 10 กับ humidity เป็น 20 ผมต้องใส่ URL เป็น <strong>http://localhost:3000/10/20/</strong> (อย่าลืมใส่ / ปิดท้ายเลข 20 ด้วย ผมเคยงงๆมาแล้ว)</p><p>ทีนี้เราก็จะได้ json ที่มีข้อมูลประมาณนี้ <strong>{ temperature: ‘10’, humidity: ‘20’ }</strong></p><p>มาดูต่อที่ config ตรง handler (ที่เราเคยใช้ก่อนหน้านี้) จริงๆแล้วมีไว้จัดการกับ request, response (ใน Hapi ใช้ replyในการจัดการกับ response ซึ่งมีรายละเอียดนิดหน่อยถ้าสนใจเข้าไปอ่านได้ — <a href="http://hapijs.com/api#reply-interface">hapi_reply</a>) หรือตัวใส่ logic ให้ route นั่นเอง ในที่นี้เราจะใช้ function test (ซึ่งเขียนไว้ตรงด้านบน) เพื่อทดสอบว่า ค่าที่เราส่งผ่าน URL เนี่ย มันส่งติดเป็น jsonp หรือเปล่า ส่วนตรง jsonp: ‘callback’ นั่นก็คือเราเรียกใช้ jsonp สำหรับ route นี้นั่นเอง</p><p>ย้อนกลับไปดู function test ที่เขียนไว้ด้านบน</p><pre>var parts = {<br>temperature: request.params.temperature,<br>humidity: request.params.humidity<br>};</pre><p>จะเห็นว่าเราเปลี่ยนตรง request มันมาเก็บไว้ใน parts และสามาถเรียกเก็บได้ด้วยการเติมชื่อตัวแปรต่อท้าย อย่าง temperature เราก็แค่ใช้ <strong>request.params.temperature</strong> ทีนี้มันทำให้ส่วนนี้เราสามารถใช้เป็น logic ในการโยนเข้าไปใน database ของเราได้</p><p>ส่วนตรง return reply ตรงนี้เป็นการแสดงข้อมูลไปที่หน้าเพจ ในที่นี้แสดงข้อมูลของ parts ซึ่งหลังจากเราทำการพิมพ์ URL <strong>http://localhost:3000/10/20/</strong><br>เราจะเห็นว่าได้มีข้อมูลแสดงตรงหน้าเพจเป็น</p><pre>{&quot;temperature&quot;:&quot;10&quot;,&quot;humidity&quot;:&quot;20&quot;}</pre><h3>2.ว่ากันด้วยเรื่องของ <a href="https://www.firebase.com/">Firebase</a></h3><p>ถ้าใครได้ดูงาน google I/O ที่ผ่านมามีการพูดถึง firebase ด้วย แล้ว Firebase คืออะไร? ถ้าเข้าไปอ่านใน wiki มีคำอธิบายไว้ว่า</p><blockquote>Firebase is a cloud services provider and backend as a service company.</blockquote><p>ซึ่งเอาเข้าจริงแล้วที่เราจะใช้ในรอบนี้มันคือ real time database ซึ่งเป็นส่วนนึงของ service</p><blockquote>Firebase’s primary product is a realtime database which provides an API that allows developers to store and sync data across multiple clients.</blockquote><p>เอาง่ายๆคือเราจะใช้ firebase ในการเป็น database เก็บข้อมูล ซึ่งในที่นี้คือเก็บข้อมูลลงบนคลาวด์ สิ่งที่ควรรู้อีกอย่างก็คือ firebase มันเป็น noSQL ถ้าเคยใช้ noSQL ตัวอื่นมาก่อนแล้วก้เข้าใจได้ไม่ยาก (เช่น MongoDB, Cassandra, ฯลฯ) คือพวกนี้มันจะไม่เก็บข้อมูลเป็นตาราง แต่มันจะเก็บเป็น key-value pair แทน ซึ่งในเว็บ firebase เองก็บอกไว้ว่าเป็น JSON</p><blockquote>Data in your Firebase database is stored as JSON and synchronised in realtime to every connected client.</blockquote><p>ก่อนอื่นเราต้องลงทะเบียนสมัครสมาชิกของ firebase ก่อนครับ ถ้าลงทะเบียนแล้วก็ log in เข้าไป ทีนี้เราก็สร้าง app ใหม่ ตรงนี้เลยฮะ</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/342/0*U49oLV6PNmJEI0y9." /></figure><p>ช่อง name คือเราตั้งชื่อให้กับ app ซึ่งโดยปกติมันจะทำให้ตรงกัน แต่เราจะปรับเองก็ได้ ซึ่งพยายามคิดชื่อให้อย่าซ้ำกับชาวบ้าน</p><p>หลังจากกรอกข้อมูลเรียบร้อยเสร็จเราก็จะได้ที่เอาไว้สำหรับเก็บข้อมูลสำหรับโปรเจคเราแล้ว จากนั้นเราก็กด Manage Appหลังจากเข้าไปแล้วจะพบว่าข้อมูลมันโล่งเลย “This location is empty!” ซึ่งนั่นก็ถูกแล้ว เพราะเรายังไม่ได้โยนข้อมูลเข้าไปเก็บมันเลย ขั้นต่อไปคือเชื่อม Hapi เข้ากับ Firebase</p><p>ก่อนอื่นเลยเราต้องติดตั้ง firebase ก่อน</p><blockquote>npm install –save firebase</blockquote><p>จากนั้นเราก็ใส่บรรทัดนี้เข้าไปใน server.js เพื่อเป็นการเรียก firebase</p><pre>const firebase = require(‘firebase’);</pre><p>ทีนี้เราต้องใส่การอ้างอิง (reference) จาก server เราไปยังที่อยู่ของ app ใน firebase ที่เราสร้างไว้ ซึ่งที่จริงแล้วมันจะมีประเด็นเรื่องความปลอดภัยด้วย เพราะการเซ็ตตาม tutorial เป็นไปเพื่อความง่าย ซึ่งทำให้คนอื่นสามารถส่งอะไรเข้าฐานข้อมูลได้ ฉะนั้นถ้าจะทำต่อจริงจังควรศึกษาเรื่องการจัดการกับ security และ rules ด้วย</p><p>เราเข้าไปที่ app ที่เราได้สร้างไว้แล้วนำ URL มาใส่ (สมมุติอันนี้ตั้ง name ชื่อ “hfa-article”)</p><pre>const myFirebaseRef = new firebase(“https://hfa-article.firebaseio.com/&quot;);</pre><p>ในส่วน ฟังก์ชัน test อย่างที่เคยบอกไว้ว่ามันเป็นส่วนที่เราจะโยนข้อมูลจาก jsonp เนี่ยลงไปใน database เราก็เขียนโค้ดด้านล่างลงไปใน test การทำงานมันตอนแรกก็ด้วยการสร้างหัวข้อย่อยจากตัว app (สร้าง child นั่นเอง คือเป็น child ของ app) โดยเรากำหนด child สองตัวตามตัวแปรที่เราจะโยนมา จากนั้นเราก็โยนด้วยการใช้ <strong>.push</strong> จะเห็นว่าเราเอา object ชื่อ parts ที่สร้างไว้ตอนแรกมาโยนเข้าไป (หรือเอาจริงๆจะโยน <strong>request.param.temperature</strong> ก็ยังได้) โดยโยนเข้าไปในคีย์ “time”</p><pre>var tempRef = myFirebaseRef.child(“temp”);<br>var humidRef = myFirebaseRef.child(“humid”);<br>tempRef.push({“time”: parts.temperature});<br>humidRef.push({“time”: parts.humidity});</pre><p>จากนั้นกด save แล้ว npm start พิมพ์ URL</p><blockquote>localhost:3000/10/20/ กด enter</blockquote><p>หลังจากมันโผล่มาในหน้าจอแล้วให้ลองไปดูที่ app ใน firebase ของเรา ถ้าไม่มีอะไรผิดพลาดข้อมูลก็ควรจะถูกส่งมาแล้ว</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/464/0*rOqxHEVkN0BZbpur." /></figure><h3>3.<a href="https://angularjs.org/">Angular </a>+ Firebase (ใน client-side)</h3><p>Angular คือ framework (ในขณะที่ React, jquery เป็น library) ตัวหนึ่งที่เอาไว้จัดการในส่วน client-side หรือก็คือหน้าเพจเรา มันถูกสร้างมาเพื่อตอบสนองต่อความท้าทายของการทำเว็บแอพซึ่งในยุคปัจจุบันมีแนวโน้มที่หน้าเว็บหนึ่งๆจะมีความซับซ้อนมากขึ้น</p><blockquote>“AngularJS (commonly referred to as “Angular” or “Angular.js“) is an open-source web application framework mainly maintained by Google and by a community of individuals and corporations to address many of the challenges encountered in developing single-page applications.” — <a href="https://en.wikipedia.org/wiki/AngularJS">wikipedia</a></blockquote><p>ส่วนถ้าใครเคยเขียน jQuery มาก่อน ผมแนะนำให้เข้าไปอ่านในนี้ครับ น่าจะทำให้เข้าใจได้ไวขึ้น <a href="http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background#15012542">“Thinking in AngularJS” if I have a jQuery background?</a></p><p>ขอเกริ่นไว้แค่นี้ก่อนฮะ ถ้าสนใจก็ลองหาอ่านเพิ่มกันดู โอเคเรามาต่อเรื่องของเรากัน ตอนนี้เรามีตัวเก็บข้อมูลแล้ว ระบบหลังบ้านก็พร้อมแล้ว ได้เวลาจับข้อมูลมาแสดง ซึ่งในส่วนนี้เราจะใช้ firebase ทำงานร่วมกับ angular นะฮะ</p><p>ก็เปิดไฟล์ index.html ที่เราเอาไปใส่ไว้ในโฟลเดอร์ public ในตอนแรก ซึ่งตอนนี้ยังมีแค่ Hello world อยู่</p><p>เพื่อความสะดวกของคนเขียนบทความเองจึงขอยกโค้ดมาทั้งหมดก่อนแล้วไว้อธิบายแยกทีหลัง</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3779617ad9cf5cb0cc145b0ccfdf35ef/href">https://medium.com/media/3779617ad9cf5cb0cc145b0ccfdf35ef/href</a></iframe><p>จะเห็นว่ามีการกำหนด reference ก่อน</p><pre>// database reference<br>var ref = new Firebase(“https://hfa-article.firebaseio.com/&quot;);<br>var tempRef = ref.child(“temp”);<br>var humidRef = ref.child(“humid”);</pre><p>โดยเริ่มจาก ref เป็นการกำหนดตัวอ้างอิงไปยัง url ที่เราได้สร้าง app บน firebase service จากนั้นพวก tempRef และ humidRef เป็นการอ้างอิงไปยังตัวลูก temp และ humid</p><p>จากนั้นเราจะไปเรียกข้อมูลมา ซึ่งเท่าที่ดูใน <a href="https://www.firebase.com/docs/web/guide/retrieving-data.html">guide</a> ผมต้องการข้อมูลในลักษณะ list อะไรทำนองนั้น จึงต้องเรียก event ที่ชื่อ <strong>“<em>child_added</em>“</strong></p><blockquote>The <strong><em>child_added</em></strong> event is typically used when retrieving a list of items from the database. Unlike <strong><em>value</em></strong> which returns the entire contents of the location, <strong><em>child_added</em></strong> is triggered once for each existing child and then again every time a new child is added to the specified path.</blockquote><p>ซึ่งใช้ร่วมกับ .on สมมุติว่าผมอยากได้ข้อมูลของ temp -&gt; tempRef.on(“child_added”);</p><p>ซึ่งจริงๆแค่นี้ก็ใช้งานได้ แต่เพื่อกันเหนียวหรือเพื่อเช็คว่ามีการเรียกข้อมูลเกิดขึ้นจริงหรือมีเออเร่อเกิดขึ้นตรงไหนหรือเปล่า ผมเลยต้องเพิ่มไปสองฟังก์ชัน(callback function) นั่นก็คือ function สำหรับเช็คค่า และ function สำหรับ handle error ดูอันแรกก่อนตรงที่เราใส่ parameter เป็น <strong>snapshot</strong> ตัวนี้เป็นตัวเอาไว้เช็คว่ามีการส่งค่าเกิดขึ้นจริง ซึ่งต้องใช้ร่วมกับ <strong>val()</strong> ซึ่งมันจะคืนค่ามาเป็น <strong>JavaScript object</strong> ของข้อมูลที่เราโยงไว้กับตัวอ้างอิง (ในที่นี้ก็คือ tempRef ซึ่งอ้างอิงไปยังตัวลูก temp ของแอพ hfa-article) เนื่องจากเรามีตัวลูกชื่อ time อยู่ข้างใน temp อีก ตอนพิมพ์ค่าออกมาเราเลยใช้เป็น <strong>snapshot.val().time</strong></p><p>ฉะนั้นโค้ดที่ได้ออกมาเลยเป็น</p><pre>tempRef.on(“child_added”, function(snapshot) {<br>console.log(snapshot.val().time);<br>}, function (errorObject) {<br>console.log(“The read failed: “ + errorObject.code);<br>});</pre><p>ต่อมาเราจะทำการใส่ Firebase ลงไปใน Angular (ซึ่งตรงนี้จะใส่ได้ เราต้องติดตั้ง AngularFire ซึ่งเราได้ทำการใส่ไปแล้ว ตรง head) โดยตั้งชื่อ app ของ Angular ว่า “sampleApp”</p><pre>var app = angular.module(“sampleApp”, [“firebase”]);</pre><p>ซึ่งหลังจากนี้จะทำให้เราสามารถโยนข้อมูลทีไ่ด้รับจาก tempRef และ humidRef ได้ โดยผ่านทาง $scope และ $firebaseArray (โดย $scope เป็น service ของ Angular เอาไว้จับคู่ข้อมูลแบบ <strong>2-way data binding</strong> และ <strong>firebaseArray</strong> เอาไว้แปลง list ข้อมูลที่ได้จาก reference มาเป็น array) ยกตัวอย่าง</p><pre>$scope.temps = $firebaseArray(tempRef);</pre><p>จะอธิบายได้ว่า นำข้อมูลที่ได้จาก tempRef (ซึ่งข้อมูลได้เป็น list จาก event ชื่อ “<strong>child_added</strong>”) ผ่านเข้าไปใน <strong>firebaseArray</strong> ก่อนที่จะจับมันโยงเข้ากับ <strong>$scope.temps</strong> ซึ่งตรงนี้จะนำข้อมูลไปจับคู่กับตัวแปร temps ใน HTML (เดี๋ยวเราจะมาอธิบายกันอีกที) — (ดูเพิ่มเติมเรื่อง <a href="https://github.com/angular/angular.js/wiki/Understanding-Scopes">scope</a> ของ AngularJS)</p><p>จากนั้นเราตั้งชื่อส่วน controller ตรงนี้ว่า “sampleCtrl” จะได้โค้ดเป็น</p><pre>app.controller(“sampleCtrl”, [“$scope”, “$firebaseArray”,<br>function($scope, $firebaseArray) {<br>$scope.temps = $firebaseArray(tempRef);<br>$scope.humids = $firebaseArray(humidRef);<br>}]);</pre><p>จบจากส่วน script เรามาดูในส่วน body จะเห็นว่ามี attribute คือ ng-app อยู่ ไอ่ตรงนี้มันเอาไว้กำหนดว่าเราจะให้ส่วนไหนใน html อยู่ในขอบเขตของ app บ้าง ซึ่งนั่นก็คือเราจะให้ sampleApp ที่เรากำหนดไว้ทำงานในระดับไหนบ้าง</p><blockquote>ng–app : This directive is used to flag the html element that Angular should consider to be the root element of our application. This gives application developers the freedom to tell Angular if the entire html page or only a portion of it should be treated as the Angular application. –<a href="https://docs.angularjs.org/tutorial/step_00">angularJS_tutorial</a></blockquote><p>จากนั้นเรามาดูตรง ng-controller นั่นก็คือเรากำลังเรียก controller ที่ชื่อ sampleCtrl เราก็ตามไปดูตรง script ว่ามันทำอะไรบ้าง จะเห็นว่ามันมี scope ที่ตอนนี้มีข้อมูลของ temperature และ humid อยู่ในรูป array เราจะต้องนำมาใช้ต่อ โดยการใช้ ng-repeat โดย “temp in temps” หมายความว่าใน array ที่ชื่อ temps <strong>($scope.temps)</strong> เรากำหนดให้ตัวแปร temp แทนตัวลูกแต่ละตัวใน array โดย <strong>{{temp.time}}</strong> ก็แสดงข้อมูลของ key ที่ชื่อ time (โดยที่ temp ใน {{ }} คือตัวเดียวกับ temp ที่เราตั้งชื่อ ใน “temp in temps”<br>– เรื่อง ng-controller, ng-repeat อ่านเพิ่มเติมได้ <a href="https://docs.angularjs.org/tutorial/step_02">angularJS_tutorial</a></p><p>ซึ่งหากไม่มีอะไรผิดพลาดก็ npm start จากนั้นเข้าไปที่ <a href="http://localhost:2000/test">http://localhost:2000/test</a> จะได้ตาม screenshot</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/0*_mvmS7PiETrgfdO6." /></figure><p>ก็เอวังด้วยประการฉะนี้</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c494855f4d0d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[เขียนตอบโต้ Bayesian’s Lovers’ Dilemma ของคุณ Suraporn]]></title>
            <link>https://medium.com/@bullrish/%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99%E0%B8%95%E0%B8%AD%E0%B8%9A%E0%B9%82%E0%B8%95%E0%B9%89-bayesian-s-lovers-dilemma-%E0%B8%82%E0%B8%AD%E0%B8%87%E0%B8%84%E0%B8%B8%E0%B8%93-suraporn-c681e94148d4?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/c681e94148d4</guid>
            <category><![CDATA[love]]></category>
            <category><![CDATA[game-theory]]></category>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Mon, 07 Mar 2016 03:25:02 GMT</pubDate>
            <atom:updated>2016-03-09T21:44:27.838Z</atom:updated>
            <content:encoded><![CDATA[<p>เผื่อใครยังไม่ได้อ่านต้นฉบับ ผมแนะนำให้อ่านก่อนครับ</p><p><a href="https://www.facebook.com/suraporn.menn.koetsawang/posts/10153735941814713">เรื่องเกือบสั้น สำหรับ วันวาเลนไทน์... - Suraporn Menn Koetsawang | Facebook</a></p><p>ผมว่า Bayesian’s lover’s dilemma มันมีปัญหาอย่างนึง คือเงื่อนไขมันต้องขยายเป็นเกม confess/silent + love/lose คือเราไม่สามารถบอกว่าคนที่ confess นั้นจะเป็นฝ่ายแพ้ได้จริงๆหากอีกฝ่าย silent เพราะมันก็มีโอกาสที่ฝ่าย silent จะบอกว่า “ค่ะ ชั้นรักคุณเช่นกัน” ถ้าเป็นเช่นนี้ก็ถือว่า win-win ทั้งคู่ เนื่องจากการที่ฝ่ายใดฝ่ายหนึ่ง silent มันไม่ได้สะท้อนถึงว่ารักไม่รัก ในแง่นี้มันน่าจะสะท้อนเรื่องกล้าหรือไม่กล้าซึ่งเป็นผลโดยตรงมาจากความกลัวว่าผู้เล่นอีกคนคิดยังไงกับเรา นั่นนำมาสู่เกมใหม่(เสนอโดยข้าพเจ้าเอง) ชื่อว่าเกมเดาใจ</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nuEg1FkFRLH3dA-ytue9ZQ.jpeg" /></figure><p>ก่อนอื่นคงต้องตั้งเงื่อนไขก่อน โดยคนที่ confess นั้นก็เป็นการบ่งบอกว่าชอบอีกฝ่าย (ในแง่นี้ให้เป็นแบบนั้น เราจะตัดกรณีที่การ confess เป็นไปเพื่อผลอื่นเช่น “เลิกยุ่งเราเหอะ เราไม่น่าไปได้ไกลเกินเพื่อนนะ” เป็นต้น) ซึงทำให้ confess จะถือเป็นการบอกรักเท่านั้น แต่ทีนี้ถ้ามาคิดต่อในโลกความเป็นจริง การที่จะ confess พร้อมกันทั้งคู่นี่มันแทบจะเป็นไปไม่ได้ (คือคงมี แต่ยาก) ลองนึกภาพชายหญิงที่จู่ๆสารภาพพร้อมกันสิ ยากมาก ฉะนั้นนี่เป็นแรร์เคสมากๆ เพื่อลดความซับซ้อนเราคิดว่าเราสามารถตัดส่วนนี้ออกได้ เพราะมันไม่น่าจะเกิดขึ้นจริง ทีนี้เราคิดว่าเราสามารถใช้หัวใจเป็น value ในเกมนี้ได้ กล่าวคือถ้าทั้งคู่รักกัน ก็จะ +1 ทั้งคู่ส่วนถ้าเกิดมีใครคนใดคนนึงไม่รัก คนที่สารภาพก็จะได้ -1 ส่วนคนที่ silent ให้ถือว่าเป็น -0.5 (เฉลี่ยจาก ความรู้สึกเฉยๆซึ่งเป็น 0 กับความรู้สึกเสียใจจากเหตุผลใดก็ตามจาก -1) ส่วนถ้าเป็นเคสที่ silent ทั้งคู่ ก็ให้เป็น 0 ทั้งคู่ เพราะต่างฝ่ายต่างก็ไม่รู้อะไรเลย</p><p>จากรูปจะเห็นว่าเกมมันค่อนข้างพิลึกพิลั่นพอสมควร แต่มันน่าจะทำให้เห็นภาพว่าทำไมอย่างน้อยประชากรกลุ่มนึงถึงเลือกจะ silent ทั้งคู่ เพราะการทำแบบนี้มันไม่ต้องเสี่ยงเจอสภาวะ -1 แต่ก็นั่นแหละถ้าเกิดว่าจะเสี่ยงสารภาพไป คุณก็ต้องรอดูว่าจะ +1 ทั้งคู่ไหม แต่แน่นอนว่าการที่ใครคนใดคนนึงยอมสารภาพระยะยาวแล้วมันย่อมดีกว่าล่ะนะ (อย่างน้อยก็ยังเหลือผลรวม 0.5 ซึ่งไอ่ตรงนี้ก็มองเอาว่าไปเจอคนใหม่ที่คู่กับตัวเองละกันครับ — ถึงแม้จะเป็น 0.5 ที่ได้จาก silent ก็ตาม)</p><p>“Whereof one cannot speak, thereof one must be silent.”<br>- Ludwig Wittgenstein</p><p>ปล.ผิดตรงไหนก็แย้งได้นะฮะ มือสมัครเล่นฮะ</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c681e94148d4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Strategist (Cynthia A. Montgomery, 2012)]]></title>
            <link>https://medium.com/@bullrish/the-strategist-cynthia-a-montgomery-2012-ea745db163b6?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/ea745db163b6</guid>
            <category><![CDATA[ikea]]></category>
            <category><![CDATA[design]]></category>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Sat, 16 Jan 2016 22:15:51 GMT</pubDate>
            <atom:updated>2016-01-16T22:15:51.966Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>(เล่มแปลของ WeLearn โดย วิญญู กิ่งหิรัญวัฒนา)</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/341/0*GAw67HHwxUm-T-Gv.jpg" /></figure><p>ก่อนอื่นต้องทำความเข้าใจคำว่ากลยุทธ์ของ Cynthia ก่อนครับ ผมจะยกข้อความในหนังสือเพื่อปูทางคร่าวๆสำหรับแนวคิดเบื้องต้นของ Cynthia “บางคนก็บอกว่าก่อนหน้านั้นพวกเขามองกลยุทธ์คือโจทย์ชุดหนึ่งที่ต้องหาทางแก้ให้ได้ ซึ่งเป้นมุมมองที่แพร่หลายทั้งในแวดวงวิชาการและในโลกธุรกิจ แต่ตอนนี้พวกเขามองว่ากลยุทธ์คือวิถีชีวิตของผู้นำ เป็นชุดคำถามที่พวกเขาต้องใช้ชีวิตอยู่กับมัน”</p><p>หนังเปิดเรื่องมาด้วยการตบหน้าฉาดใหญ่ถึงความมั่นอกมั่นใจแบบผิดๆของผู้บริหาร โดยเฉพาะแนวคิดที่ว่า ผู้บริหารที่มากความสามารถจะประสบความสำเร็จได้ทุกสถานการณ์ (ซึ่งแนวคิดนี้โดนโจมตีอย่างตรงไปตรงมาว่า “เป็นความหลงผิดคิดว่าตนไร้เทียมทามในหมู่ผู้บริหารชาวอเมิรกัน) ซึ่ง Cynthia ยกเคสของริชาร์ด แมนูเจียน CEO ของ แมสโคคอเปอเรชัน (Masco) บริษัทผลิตก็อกน้ำผู้สร้างนวัตกรรมจากก๊อกน้ำท่อเดียวจนสามารถเติบโตอย่างมากได้ (ใช่แล้วเขามีกลยุทธ์สร้างความแตกต่างในด้านนี้) Cynthia ตั้งคำถามกับผู้อ่านว่า ถ้าคุณเป็นริชาร์ด คุณจะกระโดดเข้าไปในธุรกิจอย่างเฟอร์นิเจอร์หรือไม่ สำหรับริชาร์ดคำตอบคือใช่ และนี่คือหายนะอย่างใหญ่หลวง เรื่องราวต่อจากนี้หาอ่านได้หนังสือ จะไม่ขอยกมาเพราะคงจะยาวไปสำหรับรีวิว</p><p>แน่นอนการเริ่มต้นมาด้วยการตบหน้าฉาดใหญ่ มันมักตามมาด้วยการลูบหลัง หรือเอาจริงๆแล้วการตบหน้าเป็นการปลุกหให้ตื่นจากภวังค์หรือความหลงผิดก็ย่อมได้ (ถ้าไม่เกิดอคติอย่างใหญ่หลวงซะก่อน) Cynthia นำผู้อ่านเข้าสู่บทเรียนในขั้นต่อไปด้วยการยกเคสของ IKEA ขึ้่นมา ใช่แล้ว IKEA เป็นบริษัทเกี่ยวกับเฟอร์นิเจอร์ที่ประสบความสำเร็จอย่างมหาศาล ผู้เขียนยก IKEA ขึ้นมาเพื่อจะบอกว่ามีอะไรที่ริชาร์ดพลาดไปสำหรับอุตสาหกรรมนี้ (ซึ่ง อุตสาหกรรมเฟอร์นินเจอร์นั้นมีปัจจัยภายนอกมากมายที่ทำให้ธุรกิจนั้นเติบโตได้ยาก Cynthia เน้นย้ำถึงความหลงผิดของผู้นำอยู่บ่อยๆที่คิดว่าธุรกิจเฟอร์นินเจอร์นั้นสามารถจัดการได้ด้วยการบริหารจัดการที่ดี และความสามารถของผู้นำ ซึ่งจริงๆแล้วก็ไม่ถูกซะทีเดียว ถ้าคุณไม่มีกลยุทธ์ที่ดี นั้นนำมาซึ่งหายนะเป็นแน่) และปัจจัยอะไรที่ทำให้ IKEA เติบโตมาได้ขนาดนี้ โดยผู้เขียนเสนอว่าสิ่งที่จำเป็นมากๆในจุดนี้คือการตั้งเป้าหมายและกำหนด vision บริษัทให้ชัดเจน โดยคุณต้องบอกได้ว่าบริษัทเป็นอะไร จะทำอะไร แตกต่างกับเจ้าอื่นๆอย่างไร (และแน่นอนของพวกนี้ต้องชัดเจนในระดับที่คนอื่นอ่านสามารถซึมซับและเข้าใจได้ทันที)</p><p>เคสต่อมาที่ Cythia ยกมาพูดถึง เป็นกรณีของกุชชีตั้งแต่จุดเริ่มต้นของการก่อตั้งบริษัท ยุครุ่งเรือง ยุคตกต่ำ และกลยุทธ์ของเด โซเล ในการแก้สถาณการณ์ บทนี้อ่านสนุกมากกว่าบทอื่นเป็นพิเศษ เพราะเรื่องราวของกุชชีที่เป็นบริษัทที่ดำเนินธุรกิจแบบครอบครัว มีความแตกแยกแตกหักไปจนถึงคราวที่บริษัทเกือบล่มสลาย ในบทนี้ Cynthia จะอธิบายวิธีที่เด โซเล แก้เกม และวิธีการสร้างระบบคุณค่าให้บริษัทกุชชีจนสามารถย้อนคืนสู่ความรุ่งเรืองได้ โดยบทนี้ Cynthia จะบอกว่าแค่จุดมุ่งหมายที่ชัดเจนของบริษัทยังไม่เพียงพอ มันยังมีอย่างอื่นให้นักกลยุทธ์ได้ขบคิดอีก</p><p>ต่อจากเคสนี้ Cynthia จึงพูดถึงวิธีการเขียนแผนกลยุทธ์รวมทั้ง key ที่สำคัญ โดยจะเน้นไปที่ระบบการสร้างคุณค่า และการเขียนวงล้อกลยุทธ์ โดยเรามีความรู้สึกว่า Cynthia จะชอบพูดถึงความ practical ของข้อมูลและให้น้ำหนักกับข้อมูลในการวางกลยุทธ์มากกว่าจะมาจากแรงบันดาลใจหรือความเชื่อส่วนตัว เช่นตอนที่เด โซเลกอบกู้กุชชี เด โซเล ก็ใช้ข้อมูลว่ากุชชีขายอะไรได้ และเจาะไปที่จุดนั้นมากกว่าจะให้เป็นเรื่องของแค่การตลาดและแรงศรัทธา</p><p>จากนั้นก็มาถึงบทที่สนุกมากของหนังสือเล่มนี้คือเรื่องของจ็อบและแอปเปิล โดยหนังสือเล่าตั้งแต่จ็อบก่อตั้ง ยันทำลายบริษัทตัวเอง ไปจนถึงวิธีการที่เขากอบกู้แอปเปิลขึ้นมาจากเรือที่ใกล้จะอัปปางเป็นเรือใหม่ (ตรงนี้มีการเล่าถึง the logic of growth ด้วย เก๋มาก)</p><p>โดยสรุปเป็นหนังสือที่พูดถึงกลยุทธ์ในน่าสนใจ มีเคสตัวอย่างที่ชัดเจน และเรื่องเล่าที่อ่านได้เพลิน แต่มันอาจสั้นไปหน่อย และดูไม่ practical เท่าที่คิดไว้ แต่ถ้าถามว่าเป็นหนังสือที่นักกลยุทธ์ควรอ่านไหม ตอบว่าใช่ ควรอ่าน</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ea745db163b6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zero to one( Peter Thiel )]]></title>
            <link>https://medium.com/@bullrish/zero-to-one-peter-thiel-4d40905c7e1?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/4d40905c7e1</guid>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Sat, 16 Jan 2016 22:14:22 GMT</pubDate>
            <atom:updated>2016-01-16T22:18:20.567Z</atom:updated>
            <content:encoded><![CDATA[<p>: This is Thiel’s masterpiece</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/407/0*EaO7G_C0z1mIFt-T.jpg" /></figure><p>zero to one เป็นหนังสือว่าด้วยการสร้างธุรกิจจาก 0 เป็น 1 เล่มนี้ของ Thiel ต่างจากงานเขียนในประเภทนี้ทั่วไปในแง่ที่ว่า มันไม่ใช่หนังสือแนว business ที่ให้กำลังใจอะไรเลยสักนิด แถมยังสับความคิดในการก่อตั้งธุรกิจของคุณแตกออกเป็นชิ้นเล็กชิ้นน้อย ความมั่นใจในการท้าทายต่อโลกของคุณ จะถูกท้าทายด้วยแนวคิดของ Thiel ซึ่ง Thiel คาดว่าหลังจากความคิดคุณถูกสับแล้ว คุณจะสามารถประกอบมันขึ้นมาใหม่ได้ดีกว่าเดิม</p><p>ความน่าสนใจของ zero to one นั้นมีหลายประการ</p><p>อย่างแรกมันมาจากประสบการณ์โดยตรงของ CEO บริษัทจ่ายเงินผ่านอีเมลชื่อดัง PayPal, ด้วยความที่ Thiel คลุกคลีอยู่ในวงการมานาน นั่นทำให้เขามีความสามารถและประสบการณ์สูง (เราอาจยกตัวอย่างที่เขาไปลงทุนใน Facebook นี่เป้นความสามารถในการมองที่ข้าพเจ้าอยากได้)</p><p>อย่างที่สอง เนื้อเรื่องไม่ได้มีความเกรงใจใดๆทั้งสิ้น Thiel ปล่อยหมัดโจมตีแนวคิดธุรกิจต่างๆได้อย่างมองเห็นภาพ และโจมตีมันตรงๆเช่น เรื่องที่ว่า กูเกิลนั้นจริงๆแล้วเป็นบริษัทผูกขาดที่ทำตัวไม่ผูกขาด ด้วยการบอกว่าตัวเองเป็นบริษัทที่ทำธุรกิจด้านเทคโนโลยี (ซึ่งทำให้ภาพของบริษัทดูเป็นส่วนเล็กในตลาดอันกว้างใหญ่ แทนที่จะบอกว่าตัวเองเป็นเสิร์ชเอนจิ้นที่กินตลาดนี้ไปเกือบหมดแล้ว) หรืออย่างการกัด Yahoo! ที่ Thiel ตั้งคำถามว่าตกลงแล้ว Yahoo! จะขายอะไรกันแน่</p><p>อย่างที่สาม Thiel ค่อนข้างกว้างขวางในองค์ความรู้พอสมควร อย่างน้อยเขาก็รู้ว่ากำลังพูดถึงอะไร และเขารู้กว้างพอที่จะดึงเรื่องหลากหลายมาเพื่อซัพพอร์ตแนวคิดของเขา ซึ่งเราคิดว่ามันเจ๋งมากห เช่นดึงคำพูดของมาร์กซ์ที่ชื่นชมเทคโนโลยี เปรียบเทียบความคิดของนักปรัชญาสำนักต่างๆ ซึ่งนานๆทีเราจะเห็นอะไรแบบนี้ในหนังสือแนว business แต่มันก็เข้าใจได้ว่า Thiel นั้นอาจเป็นพวกศรัทธาในความสามารถสุดๆ จนลืมไปว่า ความโชคดีก็อาจจะเป็นปัจจัยหนึ่งด้วย แม้เขาจะปฏิเสธแค่ไหนก็ตาม (เช่น ถ้าเขาสอบติดต่อในด้านกฎหมายสูงขึ้นไป เขาก็คงไม่มีทางได้มาทำไอ่นี่แน่ๆ ฉะนั้นเรื่องพวกนี้มันมีบางอย่างมากกว่าที่เราคิดเสมอ)</p><p>อย่างที่สี่ Zero to one ไม่ใช่แค่หนังสือที่รวบรวมแนวคิดดาดๆ แต่มันยัง practical อีกด้วย คือคุณสามารถนำไปขบคิดธุรกิจ start-up ของคุณได้ และนี่เป้นโน้ตของ Theil ที่ว่าด้วยตั้งแต่การหาเสาะหา idea ยันการหาเพื่อนร่วมทีม และไปถึงการขายผลิตภัณฑ์ของคุณ! ซึ่งมันมีทุกขั้นตอนที่เหล่า start-up จะประสบพบเจอ</p><p>Thiel มองว่าการผูกขาดนั้นมีหลายแบบ และมีส่วนที่สร้างสรรค์อยู่ด้วย (ซึ่งในหนังสือจะเน้นด้านนี้) เช่นการผูกขาดในรูปแบบหนึ่งอาจหมายความบริษัทนั้นพัฒนาผลิตภัณฑ์จนดีไม่มีบริษัทอื่นเทียบทานได้ (เช่น google หรือ Apple) ในแง่นี้การผูกขาดจึงดูเป็นสิ่งทื่เข้าท่าสำหรับทุนนิยมที่เข้ากันได้กับบริษัท (เพราะบริษัทนั้นต้องการกำไร ในขณะที่ทุนนิยมและการแข่งขันสมบูรณ์จะพรากกำไรจากคุณไปหมด) แต่กระนั้น Thiel ก็บอกต่อว่า การผูกขาดนั้นไม่มีทางสมบูรณ์อยู่ดี เพราะโลกเรานั้นมีการเปลี่ยนแปลง ซึ่งต่างจากเกมเศรษฐี (Monopoly) ที่ตัวกระดานนั้นคงเดิม เพียงแค่คุณกวาดซื้อทั้งกระดานคุณก็ชนะแล้ว แต่โลกความเป็นจริงนั้นไม่ใช่ เพราะบริษัทใหญ่แค่ไหนก็สามารถถูก chalenge ได้ตลอดเวลา (เช่น facebook ผงาดขึ้นมา chalenge แข่งกับ Google) ฉะนั้นในโลกที่มีการเปลี่ยนแปลงอยู่ตลอด ก็ย่อมสามารถเกิดนวัตกรรมขึ้นมาได้ตลอด หากบริษัทใดที่ผูกขาดแล้วมีแนวโน้มจะขัดขวางการก้าวหน้าของสังคม ธุรกิจนั้นถือเป็นอันตรายและพวกเราควรช่วยกันต่อต้าน แต่ประวัติศาสตร์ของความก้าวหน้าที่ผ่านมาล้วนเป็นเรื่องราวของบริษัทที่ดีกว่าเข้ามาแย่งชิงการผูกขาดไปจากบัลลังก์เดิม”</p><p>ที่ Theil เห็นด้วยกับการผูกขาดบางประการนั่นเป้นเพราะว่า การผูกขาดมันคือการสร้างนวัตกรรมใหม่ๆขึ้นมา ตามคอนเซปส์ของหนังสือนั่นคือ จาก 0 เป็น 1 เราอาจตีความว่า 0 คือการที่ไม่มีแนวคิดนี้อยู่เลย ในขณะที่ 1 คือแนวคิดของนวัตกรรมใหม่ที่ผงาดขึ้นมา และผูกขาดกับตลาดนั้นไว้ (เพราะมันไม่มีคนอื่นคิดไว้ก่อน) ฉะนั้นเมื่อไม่มีการแข่งขัน คุณจึงเติบโตไปได้ไกลสุดๆ</p><p>นอกจากนั้นเราอาจจะบอกว่า Thiel เป็นพวกมองโลกในแง่ดีและอนาคตยังพอมีความชัดเจนอยู่บ้าง กล่าวคือ Theil ไม่เชื่อในเรื่องโชคชะตาเท่าไหร่ เขาเชื่อในพลังของมนุษย์มากกว่า (เราอาจบอกว่าเขาเป็นพวกมนุษย์นิยม) ซึ่งในหนังสือ Theil โจมตีแนวคิดของ มัลคอล์ม แกลดเวลล์ ในหนังสือ Outliers (เป็นหนังสือขึ้นหิ้งที่เราชื่นชอบเช่นกัน) ว่า แนวคิดการมองของมัลคอล์มนั้น เป็นแนวคิดของพวก Baby boomer ที่มักจะพูดถึงเรื่องโชคเรื่องดวงมากกว่าจะเน้นไปที่การพัฒนาตัวเองไปถึงเป้าหมาย ซึ่ง Thiel ได้เล่าไว้ในหนังสือถึงเหตุผลเหมือนกัน ว่ามันมาจากความเชื่อเรื่องเศรษฐกิจในยุคนึงที่ถูกสั่นคลอนให้เราหนีกลับไปใน comfort zone และคิดว่าในเมื่ออนาคตมันไม่แน่นอน เราจึงต้องไร้แผน (หรืออย่างมากคือ เตรียมแผนไว้ แล้วก็รอฟลุค) ซึ่งเราชอบที่ Thiel เน้นย้ำในเรื่อง “แนวคิด” ของอนาคตที่ควรมีรูปร่างแน่นอน อย่างน้อยคุณต้องมีแนวคิดว่ามันแน่นอนก่อน แล้วทำตามแผน (Thiel ยังเชื่อว่า ในโลกธุรกิจ นั้นเหมือนการเล่นหมากรุกที่ต้องไปดูหมากท้ายๆก่อน)</p><p>อย่างที่บอกไว้ว่า Theil ออกจะมนุษย์นิยม มุมมองของ Theil ที่มีต่อโลกอนาคตในเรื่องของเทคโนโลยี Theil มองว่า ท้ายสุดแล้วมนุษย์ก็สร้างจักรกลขึ้นมาไม่ใช่เพื่อให้มันแทนที่มนุษย์ แต่เพื่อให้มันเป็นเครื่องมือผ่อนแรงต่างหาก ซึ่งเขามองไว้ว่าท้ายสุดแล้ว นวัตกรรมหรือการวิเคราะห์เชิงลึกก็ยังต้องใช้มนุษย์อยู่ดี ไม่มีเครื่องจักรอะไรจะทำหน้าที่นี้ได้สมบูรณ์ แต่เราสร้างนวัตกรรมมาเพื่อให้เราวิเคราะห์อะไรต่างๆได้ง่ายขึ้น “มุมมองอันลึกซึ้งที่นำไปใช้ได้จริงจะมาจากการวิเคราะห์โดยมนุษย์เท่านั้น”</p><p>สิ่งที่ Thiel เน้นยำ้อยู่บ่อยๆ ซึ่งเราจะเจอได้ในตลอดเล่มคือแนวคิดในการเริ่มจากครองตลาดเล็กๆก่อน อาจจะด้วยการขายหรือสร้างความสัมพันธ์ ซึ่งเราต้องผูกขาดตลาดนี้ให้ได้ นอกจากนั้น Thiel ยังบอกอีกด้วยว่าให้เรามองไกล อย่างน้อยก็ต้องวางแผนล่วงหน้า อาจจะยี่สิบปีถึงสามสิบปี เพราะถ้าคุณคิดจะทำธุรกิจจริงๆ การมองเพียงระยะสั้นๆนั้นให้ผลที่ไม่ดีมากนัก</p><p>ด้วยทั้งหมดที่ว่ามานี้ ทำให้ Zero to one นั้นมีความน่าสนใจสำหรับ Start-up หรือผู้ที่เริ่มสนใจในแวดวงนี้ น่าจะจัดได้ว่าเป็นหนังสือที่ must read</p><blockquote>Thiel พูดถึงเรื่องกฎหมายลิขสิทธิ์อยู่นิดหน่อย แต่เป็นประเด็นที่น่าสนใจ ในแง่ที่ว่ากฎหมายลิขสิทธิ์มันว่าด้วยเรื่องการผูกขาดแนวคิด “ไม่แปลกเลยถ้าจะมีคนถามว่า มันเป็นเรื่องสมควรแล้วหรือที่ใครบางคนได้รับสิทธิตามกฎหมายให้ผูกขาดเพียงเพราะเป็นคนแรกที่คิดมาได้”</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4d40905c7e1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[โอมเพี้ยง! แผลจงหาย ]]></title>
            <link>https://medium.com/@bullrish/d0152b4b4d01?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/d0152b4b4d01</guid>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Tue, 29 Oct 2013 06:01:23 GMT</pubDate>
            <atom:updated>2013-10-29T06:01:23.030Z</atom:updated>
            <content:encoded><![CDATA[<h4>ถ้อยคำที่เป็นมนตรา </h4><p>“โอมเพี้ยง เดี๋ยวแผลก็หายแล้วนะลูก” ผมจำไม่ได้ว่าเคยได้ยินประโยคทำนองนี้ครั้งแรกตอนกี่ขวบ คำปลอบใจนี้ยายผมมักกล่าวขึ้นเวลาเมื่อผมเล่นซนจนหกล้ม เลือดออก ฟกช้ำ หรือเมื่อใดก็ตามที่เกิดการบาดเจ็บ แกจะทำท่าเป่าแถวๆแผล ผมจำไม่ได้ว่าตอนครั้งแรกที่ได้ยินนั้น ตัวเองเชื่อหรือเปล่าว่าแผลมันจะหายจริงๆ</p><p>เมื่อผมโตขึ้นมาอีกหน่อย ผมได้รู้จักโลกกว้างขึ้น แต่มุมมองของผมก็คงไม่ได้กว้างตามขึ้นเลย เมื่อผมได้ยินประโยคทำนองนี้อีกครั้ง ผมกลับรู้สึกต่อต้าน และเมื่อได้ยินยายกล่าวครั้งใด ผมก็มักจะสวนกลับไปทำนองว่า “โถ่ยาย ทำแบบนั้นมันไม่หายหรอกน่า”</p><p>เมื่อไม่นานมานี้ มีอะไรสักอย่างสะกิดใจให้ผมนึกถึงคำพูดนี้ขึ้นมาอีกครั้ง ผมได้ตั้งคำถามกับตัวเองว่า มันถูกต้องแล้วหรือ ที่เถียงกลับยายไป ผมว่าท่านรู้การทำเหมือนสวดคาถาโอมเพี้ยง แผลมันก็คงไม่หายหรอก อันที่จริงแล้วดูเหมือนว่าการที่แผลหายกับการสวดคาถา มันก็ไม่เกี่ยวข้องกันเสียด้วยซ้ำ แต่ที่ท่านกล่าว ก็อาจเป็นเพราะท่านเพียงแค่อยากให้เรารู้สึกดี รู้สึกว่าจะมีสิ่งดีๆมาช่วยให้เราหายเจ็บหรือลืมมันไปสักพัก หรืออะไรก็ตามแต่ ผมคิดว่าประโยคทำนองนี้ มันทำหน้าที่ได้แค่คำปลอบใจเสียมากกว่า และมันก็คงจะไม่มากไปกว่านั้น ผมว่าคงไม่มีใครคิดจริงๆจังๆหรอกว่า ไอ่การทำแบบนี้มันจะรักษาแผลทางกายภาพได้ แต่ไม่แน่แผลทางใจอาจได้รับการเยียวยาขึ้นมาสักนิด</p><p>ฉะนั้นเวลาคุณเจอเพื่อนที่กำลังอกหัก รักร้าว คุณอาจกล่าวกับเขาด้วยประโยคทำนองนี้ “เฮ้ย ไม่เป็นไรนะเว้ย เดี๋ยวก็หาย เพี้ยง!” เพราะเพื่อนของคุณไม่แน่ว่าอาจจะอาการดีขึ้นด้วยเวทย์มนต์แห่งถ้อยคำเหล่านี้ก็เป็นได้</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0152b4b4d01" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ตัวตนของผีในโลกสมัยใหม่]]></title>
            <link>https://medium.com/@bullrish/d0c202a9065c?source=rss-59491a7bd191------2</link>
            <guid isPermaLink="false">https://medium.com/p/d0c202a9065c</guid>
            <dc:creator><![CDATA[W.S.Kiang]]></dc:creator>
            <pubDate>Tue, 22 Oct 2013 20:04:42 GMT</pubDate>
            <atom:updated>2013-10-29T14:05:19.390Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*S6DXOtX3N2JcbhOh.png" /><figcaption>http://linkleak.se/sites/linkleak/files/screens/conjuring-vodrip-x264-ac3-fookasscreen_0.png</figcaption></figure><h4>Ghost in modernity</h4><p>จริงๆเรื่องนี้ปิ๊งไอเดียหัวข้อมาได้จากการไปนั่งเรียนวิชา Localism and Globalization (ซึ่งดูเหมือนเกรดจะได้มาอย่างยากเย็น) และมานั่งเล่นเฟสเจอ อาจารย์ เจษฎาพูดเรื่องบั้งไฟพญานาคพอดี</p><p>ผมคิดว่าความเชื่อเรื่องผีสางในโลกสมัยใหม่ ได้ถูกเปลี่ยนให้กลายเป็นอย่างอื่น เป็นสิ่งอื่น กล่าวคือมันไม่ได้ทำหน้าที่ความเชื่อผีดั้งเดิมเสียแล้ว(ที่คอยกำหนดและสร้างความกลัวเพื่อควบคุมสังคม — อันนี้ในความเห็นข้าพเจ้าเอง) โดยเฉพาะสังคมเมืองที่ ปัจจุบันจะเห็นได้ว่าความเชื่อเรื่องผีสางมักถูกนำไปใช้ประโยชน์ในแง่เชิงพาณิชย์(ก็นี่มันโลกทุนนิยมนี่นะ) ยกตัวอย่างที่เห็นได้ชัดก็คือ รายการจำพวกวาไรตี้ผีๆต่างๆอย่าง คน(นม)อวดผี รายการฝรั่งอย่าง Destination truth หรือภาพยนตร์ผีๆต่างๆเป็นต้น จะเห็นได้ว่า ผี ได้กลายเป็นเครื่องบันเทิงรูปแบบหนึ่งไปแล้ว</p><p>ทุกวันนี้ด้วยความสามารถของวิทยาศาสตร์ และเทคโนโลยี ทำให้สามารถ ลดอำนาจของ ผี ที่ในอดีตเป็นเรื่องลึกลับและเป็นเรื่องของประสบการณ์ส่วนบุคคลลงไปได้ ดังที่ผู้กำกับ the conjuring เจมส์วานส์ เคยบอกไว้ประมาณว่า หนังผีของยุคนี้เนื้อเรื่องต้องย้อนอดีตไปสักหน่อย ไม่งั้นหากเป็นปัจจุบัน ใครเจอผีก็ถ่ายรูปแล้วอัพลง Instragram ทำให้ผีได้หมดความขลังลงไปเสียแล้ว ฉะนั้นผีจึงถูกลดทอนให้กลายเป็นเพียงเครื่องมือด้านอุตสาหกรรมเพื่อรับใช้ความบันเทิงและทุน ในโลกปัจจุบันนี้</p><p>แต่ถ้าเราพูดถึงชนบทจริงๆที่ห่างไกลจากเทคโนโลยีต่างๆ ผีจะยังคงสถิตอยู่ที่นั่น เพราะผีเป็นความเชื่อที่แข็งแกร่งซึ่งตกทอดมายังรุ่นต่อรุ่น และตราบใดที่ความเป็นสมัยใหม่(ทุนนิยม เทคโนโลยี เครือข่ายการเชื่อมต่อ ฯลฯ) ยังไม่เข้าครอบงำท้องถิ่นนั้น ผีก็จะยังคงสิงสถิตและคงความขลังของมันไว้ตราบนานเท่านาน</p><p>ปล.เนื้อหานี้ค่อนข้างมั่วๆหน่อยน่ะครับ notes น้อย Ref ก็น้อย ส่วนใหญ่เป็นความเห็นส่วนตัวมากกว่า<br>ปล.2 พยายามใช้สำนวนให้มันคล้ายหนังสือที่มันอ่านยากๆหน่อย แฮ่!</p><p>Note: ความเป็นสมัยใหม่คืออะไร?<br><a href="http://vorachai-juntip.blogspot.com/2011/06/postmodernism-post-modern-postmodern.html">http://vorachai-juntip.blogspot.com/2011/06/postmodernism-post-modern-postmodern.html</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0c202a9065c" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>