<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Elixir on yothinix</title><link>https://yothinix-com.pages.dev/tags/elixir/</link><description>Recent content in Elixir on yothinix</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 18 Feb 2022 11:09:22 +0700</lastBuildDate><atom:link href="https://yothinix-com.pages.dev/tags/elixir/index.xml" rel="self" type="application/rss+xml"/><item><title>A day with Elixir</title><link>https://yothinix-com.pages.dev/a-day-with-elixir/</link><pubDate>Sun, 22 Jul 2018 00:00:00 +0700</pubDate><guid>https://yothinix-com.pages.dev/a-day-with-elixir/</guid><description>&lt;p&gt;จากวันเสาร์ - อาทิตย์นี้พี่ @zkan ชวนมาทำ Hackathon กันที่ Pronto Tools เลยเป็นโอกาสเหมาะว่าจะทำอะไรที่มัน Out of comfort zone ซักหน่อยแล้วส่วนตัวผมอยากจะเขียน Elixir มานานหลายปีมากเลยเป็นที่มาของ Blog นี้ครับ&lt;/p&gt;
&lt;p&gt;เอาจริงๆ จนจบวันนี้ผมยังไม่รู้เลยว่าที่ผมเขียนไปมันถูก / ผิด idiom ของ Elixir ไปมากแค่ไหน แต่แค่อยากจะมาพูดถึงสิ่งที่ประทับใจในตัวภาษาจากมุมมองของคนที่เขียนแต่ Python เป็นหลักในชีวิต&lt;/p&gt;
&lt;h1 id="project-structure"&gt;Project Structure&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://yothinix-com.pages.dev/a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_d06454c861e2c101.webp"
srcset="https://yothinix-com.pages.dev/a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_1aa32778a41c3f0c.webp 480w, https://yothinix-com.pages.dev/a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_d06454c861e2c101.webp 960w"
sizes="(max-width: 960px) 100vw, 960px"
width="960" height="1093"
alt=""
loading="lazy" decoding="async"&gt;
&lt;em&gt;mix.exs&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;อยากแรกเลยที่เจอพอคิดว่าจะใช้ Elixir Implement คือ จะวางโครงสร้างมันยังไง จะเขียนเป็น script.ex เลย หรือต้องทำยังไง หลังจากลองผิดลองถูกอยู่ซักแปป ถึงรู้ว่าภาษามีสิ่งที่เรียกว่า Mix ซึ่งเป็น Build tools หลักของภาษาสามารถสร้างได้โดยใช้คำสั่ง&lt;/p&gt;
&lt;p&gt;$ mix new &amp;lt;name&amp;gt;&lt;/p&gt;
&lt;p&gt;ซึ่งพอ Generate ออกมาแล้วมันจะสร้าง structure project ให้เลยตามชื่อที่เราตั้ง ซึ่งมี mix.exs เป็น Project description file อารมณ์น่าจะคล้ายๆ package.json ของ Node สร้าง module เราให้ด้วยจะอยู่ใน lib/&amp;lt;name&amp;gt;.ex สร้างไฟล์เทสของ module เราให้ด้วยที่ test/&amp;lt;name&amp;gt;_test.exs รวมถึงสร้าง README.md ให้เราด้วยอัตโนมัติเลย&lt;/p&gt;</description><content:encoded><![CDATA[<p>จากวันเสาร์ - อาทิตย์นี้พี่ @zkan ชวนมาทำ Hackathon กันที่ Pronto Tools เลยเป็นโอกาสเหมาะว่าจะทำอะไรที่มัน Out of comfort zone ซักหน่อยแล้วส่วนตัวผมอยากจะเขียน Elixir มานานหลายปีมากเลยเป็นที่มาของ Blog นี้ครับ</p>
<p>เอาจริงๆ จนจบวันนี้ผมยังไม่รู้เลยว่าที่ผมเขียนไปมันถูก / ผิด idiom ของ Elixir ไปมากแค่ไหน แต่แค่อยากจะมาพูดถึงสิ่งที่ประทับใจในตัวภาษาจากมุมมองของคนที่เขียนแต่ Python เป็นหลักในชีวิต</p>
<h1 id="project-structure">Project Structure</h1>
<p><img src="/a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_d06454c861e2c101.webp"
       srcset="/a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_1aa32778a41c3f0c.webp 480w, /a-day-with-elixir/1-QgdqLpwRMsc176hrMrLAdg_hu_d06454c861e2c101.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="1093"
       alt=""
       loading="lazy" decoding="async">
<em>mix.exs</em></p>
<p>อยากแรกเลยที่เจอพอคิดว่าจะใช้ Elixir Implement คือ จะวางโครงสร้างมันยังไง จะเขียนเป็น script.ex เลย หรือต้องทำยังไง หลังจากลองผิดลองถูกอยู่ซักแปป ถึงรู้ว่าภาษามีสิ่งที่เรียกว่า Mix ซึ่งเป็น Build tools หลักของภาษาสามารถสร้างได้โดยใช้คำสั่ง</p>
<p>$ mix new &lt;name&gt;</p>
<p>ซึ่งพอ Generate ออกมาแล้วมันจะสร้าง structure project ให้เลยตามชื่อที่เราตั้ง ซึ่งมี mix.exs เป็น Project description file อารมณ์น่าจะคล้ายๆ package.json ของ Node สร้าง module เราให้ด้วยจะอยู่ใน lib/&lt;name&gt;.ex สร้างไฟล์เทสของ module เราให้ด้วยที่ test/&lt;name&gt;_test.exs รวมถึงสร้าง README.md ให้เราด้วยอัตโนมัติเลย</p>
<h1 id="task-runner">Task Runner</h1>
<p><img src="/a-day-with-elixir/1-luQtd2USnPsxivHVl2itGw_hu_e99d6e0d2c534f7f.webp"
       srcset="/a-day-with-elixir/1-luQtd2USnPsxivHVl2itGw_hu_7dda69d8dcfaa0c9.webp 480w, /a-day-with-elixir/1-luQtd2USnPsxivHVl2itGw_hu_e99d6e0d2c534f7f.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="431"
       alt=""
       loading="lazy" decoding="async">
<em>lib/mix/tasks/main.ex</em></p>
<p>คำถามต่อมาหลังจากสร้าง Project ขึ้นมาได้แล้วก็คือ แล้วจะรันมันยังไง? ผมพอจะรู้มาบ้างว่ามันมี iex (Interactive Elixir) ซึ่งเทียบได้ว่าเป็น REPL ของภาษานี้ก็ได้ แต่จะให้ iex มาสั่งรัน module เราทุกครั้งมันก็ค่อนข้างจะพิมพ์เยอะ Requirement ผมเองคืออยากได้ Task runner มาไว้รันคำสั่งให้ซึ่ง มันมีวิธีทำครับ แล้วง่ายมากด้วย</p>
<p>วิธีคือไปสร้าง Directory ตามนี้ครับ /lib/mix/tasks/ เสร็จแล้วข้างในนั้นสร้าง module มาตัวนึงชื่ออะไรก็ได้ในที่นี้ผมตั้งชื่อมันว่า main.ex ข้างใน module ก็ตามภาพข้างบนครับ จะเห็นว่ามัน use Mix.Task ประกอบกับ method <strong>run</strong> หลังจากนั้นใน method run ให้เราใส่ Method ที่เราจะรันลงไป แค่นี้ก็เรียบร้อย วิธีรันก็คือ</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ mix main
</span></span></code></pre></div><h1 id="formatter">Formatter</h1>
<p>ความเจ๋งของ mix project อีกอย่างก็คือมันมี Formatter ในตัวครับ​ ซึ่งผมก็ไม่รู้ว่ามันทำงานยังไงนะ แต่แค่เราสั่งคำสั่งข้างล่าง โค้ดเราก็จะจัดระเบียบให้เป็นไปในแนวทางเดียวกันละ ไม่ต้องลง module แยกอย่าง YAPF อะไรแบบนี้เลย</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ mix format
</span></span></code></pre></div><h1 id="configuration">Configuration</h1>
<p><em>แน่นอน</em>ว่าเวลาเราทำ App ซักแอพถ้ามีการต้องติดต่อกับ 3rd Parties มันหลีกเลี่ยงไม่ได้ครับที่เราจะต้องหาทางใช้ Secret บางอย่างเช่น​ Username, Password หรือ Access Token กับโค้ดของเรา ซึ่งปกติตอนผมเขียน Python ก็เก็บมันไว้ใน .env ให้ Pipenv มันอ่านออกมาให้ แต่ Elixir มีวิธีต่างออกไปครับ</p>
<p>วิธีของ Elixir คือเราจะสร้าง config file ขึ้นมา แล้วสร้าง config เก็บไว้เป็นกลุ่มๆ (อันนี้ก็ไม่รู้จะเรียกว่าไงเหมือนกัน) เสร็จแล้วในโค้ดของเราก็เรียกผ่าน Application.get_env(&lt;group&gt;, &lt;key&gt;) ก็เป็นอันเรียบร้อยครับ</p>
<p><img src="/a-day-with-elixir/1-MK9DTfmcebifeWkxPJC26g_hu_705faa248b2003f4.webp"
       srcset="/a-day-with-elixir/1-MK9DTfmcebifeWkxPJC26g_hu_76bb264ecabb5db7.webp 480w, /a-day-with-elixir/1-MK9DTfmcebifeWkxPJC26g_hu_705faa248b2003f4.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="622"
       alt=""
       loading="lazy" decoding="async"></p>
<h1 id="module">Module</h1>
<p>มาดูที่ Module ที่มัน Generate มาให้บ้าง ผมยอมรับเลยครับว่าแว็บแรกที่มองนี่คือ อ่อ มันก็ class นี่หว่า จริงๆ มันน่าจะเทียบกันไม่ได้โนะ แต่ Concept หลายอย่างก็ใกล้เคียงกัน อย่างแรกเลยที่ชอบคือ Attribute ครับถ้าเป็น Python เราก็เขียนมันไปโต้งๆ ใน class นะแหละเหมือนเป็นตัวแปรตัวนึง แต่กับ Elixir ตัว Module attribute เราต้องเพิ่ม @ ข้างหน้ามันครับ</p>
<p><img src="/a-day-with-elixir/1-cHqXbMropxKCOS-7zvTq5w_hu_ffd32bd8c7c6f008.webp"
       srcset="/a-day-with-elixir/1-cHqXbMropxKCOS-7zvTq5w_hu_8d4c376064f76bb5.webp 480w, /a-day-with-elixir/1-cHqXbMropxKCOS-7zvTq5w_hu_ffd32bd8c7c6f008.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="207"
       alt=""
       loading="lazy" decoding="async"></p>
<h1 id="anonymous-function">Anonymous function</h1>
<p>เอาจริงๆ ก็ไม่เคยคิดว่าจะได้เขียน Anonymous function บ่อยๆ เพราะส่วนตัวไม่ชอบเพราะอ่านไม่ค่อยรู้เรื่อง (ใน Python หนะ) แต่ใน Elixir นี่ผมค่อนข้างชอบทีเดียว เพราะมันคล้ายๆ กับ Arrow function ที่เขียนบ่อยๆ ใน JS เพียงแต่ว่ามันจะมี keyword <strong>fn</strong> เอาไว้ข้างหน้าไว้ประกาศว่ามันคือ function นะ แล้วมี end ปิดตรงท้ายด้วย หน้าตาก็ประมาณนี้ครับ</p>
<p><img src="/a-day-with-elixir/1-jAmmctD0WZ4uku0fKIOBYw_hu_ab0d615d1558424b.webp"
       srcset="/a-day-with-elixir/1-jAmmctD0WZ4uku0fKIOBYw_hu_9e03469bddb95cac.webp 480w, /a-day-with-elixir/1-jAmmctD0WZ4uku0fKIOBYw_hu_ab0d615d1558424b.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="145"
       alt=""
       loading="lazy" decoding="async"></p>
<h1 id="pipe-operator-">Pipe Operator ✨</h1>
<p>Pipe Operator (|&gt;) นี่น่าจะเป็นสิ่งที่ผมชอบที่สุดในตัวภาษานี้แล้ว และเป็นเหตุผลหลักให้อยากเขียนมาตลอดด้วย หลักการของมันง่ายๆ มากครับคือมันจะรับ return value ของ function ทางซ้าย (หรือก่อนหน้า) ของ Pipe แล้วเอามาเป็น First argument ของ function ทางขวา หรือถ้า function ตัวที่รับต้องรับ arguments มากกว่าหนึ่งตัว เราก็สามารถเขียน arguments ตัวต่อๆ ไปใส่ได้เลยครับ มันจะรู้เองว่า arguments ตัวแรกจะถูกยัดมาผ่าน Pipe</p>
<p><img src="/a-day-with-elixir/1-bnlJEc7TuC3FyNm2o2kUaA_hu_1d7a71e41b361c6b.webp"
       srcset="/a-day-with-elixir/1-bnlJEc7TuC3FyNm2o2kUaA_hu_9b576d71a9f0ed17.webp 480w, /a-day-with-elixir/1-bnlJEc7TuC3FyNm2o2kUaA_hu_1d7a71e41b361c6b.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="585"
       alt=""
       loading="lazy" decoding="async"></p>
<p>ผมพบว่าพอเราเริ่มใช้ Pipe บ่อยขึ้นเรื่อยๆ เราจะเริ่มมองทุกอย่างให้เป็น function ที่เล็กที่สุดครับ ที่เราทำแบบนั้นเพราะอยากจะ Chain ให้มันอยู่ใน Pipe แล้วมันรู้สึกดีมาก อ่านง่ายด้วย</p>
<h1 id="signils">Signils</h1>
<p>อันนี้เป็นสิ่งใหม่มากในชีวิตครับ ความอยากเขียน Elixir มาหลายปีก็ไม่เคยทำให้เรามาพบเจอกันจนกระทั่งวันนี้ Signils นี่เป็นอะไรที่ทำให้เราเขียน string, regex ฯลฯ ได้ง่ายขึ้นมากครับ หน้าตาก็ประมาณนี้</p>
<p><img src="/a-day-with-elixir/1--sALtXJN22HGJ7ElOFtk6g_hu_8c1315f76492217e.webp"
       srcset="/a-day-with-elixir/1--sALtXJN22HGJ7ElOFtk6g_hu_fb65325056e4cc46.webp 480w, /a-day-with-elixir/1--sALtXJN22HGJ7ElOFtk6g_hu_8c1315f76492217e.webp 960w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="960" height="212"
       alt=""
       loading="lazy" decoding="async"></p>
<h1 id="string-and-io">String and IO</h1>
<p>ขอบันทึกเก็บไว้หน่อยละกันว่า String ของ Elixir มีทุกอย่างครบเครื่องอย่างที่ String ในภาษาอะไรก็ตามควรจะมีเช่น Multiline string, String interpolation ให้ครบเครื่อง</p>
<p>ส่วน IO น่าจะเป็นวิธี basic ที่สุดในการ debug โนะ ซึ่งตอนแรกสุดเนี่ยผมนึกว่า <strong>IO.puts</strong> มันจะ support ทุกอย่างแต่พอเขียนไป อ่าวเห้ย ถ้าไม่ใช่ string ต้องใช้ <strong>IO.inspect</strong> ละก็ละเอียดไปอย่าง</p>
<h1 id="debugger">Debugger</h1>
<p>ถึงแม้ <strong>IO.inspect</strong> จะเป็นวิธี debug ที่ง่ายที่สุด แต่ผมยอมรับเลยว่าผมติด debugger ใน Python มาคือ <strong>import ipdb; ipdb.set_trace()</strong> แล้วพยายามอยากหาวิธีที่จะใช้ท่าเดียวกันได้ โชคดีที่ Elixir ก็มีอะไรบางอย่างคล้ายๆ กันคือ <strong>IEx.pry</strong></p>
<p><img src="/a-day-with-elixir/1-OIRm5NVjxnoeNNwuJuipWg_hu_4b7a4f169953cff5.webp"
       srcset="/a-day-with-elixir/1-OIRm5NVjxnoeNNwuJuipWg_hu_4b7a4f169953cff5.webp 480w"
       sizes="(max-width: 960px) 100vw, 960px"
       width="480" height="291"
       alt=""
       loading="lazy" decoding="async"></p>
<p>วิธีการของมันก็คือ เราไปแปะ Snippet ไว้ตรงโค้ดที่เราต้องการจะหยุดมันเสร็จแล้วเราสั่ง iex -S &lt;task&gt; มันก็จะสั่งรัน task ที่เราสร้างไว้แล้วหยุดตรงที่เราแปะ debugger ไว้ครับ มีประโยชน์มากๆ</p>
<script src="https://gist.github.com/yothinix/9b81abb90ec81c987888f9fa464a57ea.js"></script>
<h1 id="conclusion">Conclusion</h1>
<p>ถ้าจะมีอะไรที่รู้สึกชอบ จากการที่ใช้ภาษาใหม่ๆ Implement อะไรบางอย่างก็คือ มันได้แนวคิดใหม่ๆ ในการแก้ปัญหานี่แหละครับ มีประโยชน์มากๆ ตอนนี้เลยชอบเขียน utility function เล็กๆ เอาไว้แก้ปัญหาย่อยๆ แล้วเอามารวมกันเป็น Pipeline ก็แก้ปัญหาอะไรได้เร็วดี (เขียนเทสง่ายด้วย)</p>
<p>อ่อเกือบลืมบอก Hackathon โปรเจ็คเล็กๆ ผมนี่แค่ต้องการสร้างอีเมล์ Sprint Summary ตอนจบ Sprint นะครับ ปกติเราใช้ Milestone + Issue ในการทำงานที่ Pronto อยู่แล้ว แต่ถ้ามี Script นี้ก็ทำให้ลดเวลาการเขียนอีเมล์ลงไปได้บ้าง โค้ดของโปรเจ็คอยู่ที่นี่นะครับ สนใจก็เปิด PR มาแก้ได้ครับผมหรือใครจะ Fork ไปพัฒนาต่อผมก็ยินดีครับ</p>
<blockquote>
<p><a href="https://github.com/yothinix/mssum">GitHub - yothinix/mssum: Milestone Summary - A Pronto’s hackathon project</a>
Milestone Summary - A Pronto’s hackathon project. Contribute to yothinix/mssum development by creating an account on GitHub.
— GitHub, yothinix</p>
</blockquote>
<hr>
<p>Original post at: <a href="https://yothinix.medium.com/a-day-with-elixir-dc41e21d68c6">https://yothinix.medium.com/a-day-with-elixir-dc41e21d68c6</a></p>
]]></content:encoded></item></channel></rss>