• [練習] (First) Six step blues

    (First) Six steps blues

    曲子是 youtube 上的爵士藍調教學。錄下來用 pianoteq 的 Petrof Mistral Jazz Recording 轉出來試試看。 每次在彈這首的時候我總是想到膨膨跟咪咪剛到這個世界上的那個畫面。然後隨著一字一句、慢慢的跟他們說話,他們也慢慢的學會那些句子。那麼總有一天,他們會學到一些我們還不知道,甚至再無法理解的事物吧。然後把這一切加在一起,他們就能夠唱出自己的、世界上獨一無二的歌了。 我期待著那一天的到來。

  • 昨天朋友 Fred 來小碧潭親子館(?)玩,聊天聊到薩提爾的東西,他說了一句話讓我感觸很深。他說的差不多像是

    「表達情緒跟情緒勒索的差別,在於前者是單純的說出對方的行為所造成你的感受,但表達者知道之所以會有這樣的感受,有可能有部份是因為感受者自身的經歷跟狀態所造成的。

    但情緒勒索是說話的人要對方為自己產生的情緒負起全部的責任來解決它。」

    這是我聽過最棒的提醒自己的方式。

  • 聊聊 Elixir 中的 type

    最近有幾位朋友分別來問 Elixir 的 type 的問題,想說中文世界好像沒有比較完整的東西,就把知道的東西整理出來。

    (目前) Elixir 的 type 能做什麼?

    tl;dr: 最主要是文件,然後在某種程度下防止錯誤。

    我覺得這應該是在研究 Elixir 的 type 時最需要知道的事情了。不像 Haskell 及 F# 這種以型別著稱的 ML 系語言,Elixir / Erlang 本質上是個動態語言,所有與型別有關的標註都會被編譯器忽略。而 Erlang 內建的型別檢查工具 dialyzer 是採用 success typing,在型別 有可能 是正確的時候就視為通過 (等一下會看到有趣的範例),所以目前在 Elixir 中撰寫型別標注最主要還是用做於程式相互操作間的文件溝通使用。

    我自己個人的習慣通常是剛開始寫 code 時不會標注型別,然後在寫測試程式時把 public 的函式都標上型別。當然也有專注在資料結構的情況,這時候才會一開始就對型別比較講究。

    Elixir 的型別標注語法

    Elixir 的型別語法主要就看官方文件的 typespecs 這一頁。比較值得另外提的就是 sum type (union type) 跟 product type 的標記方式。在 Elixir 裡 sum type 是用 | 這個符號來分隔。而 product type 就是用 tuple 語法了。來個範例:

    defmodule Card do
      # 自定義的型別用 @type
      # 四種 atom 中的一種
      @type suit() :: :clubs | :diamonds | :hearts | :spades
    
      # 2 到 10 的數字或是四種 atom 中的一種
      @type rank() :: 2..10 | :jack | :queen | :king | :ace
    
      # product type 用 {}
      # t 表示 Remote type。這個會變成 Card module 本身的 type,
      # 可以用 Card 或是 Card.t() 來指涉這個型別
      @type t :: {rank(), suit()}
    
      # 標注函式型別用 spec
      # 接收 list of cards, 回傳 card
      @spec deal([Card]) :: Card
      # 同一個模組下也可以直接用 t 表示,改成這樣:
      # @spec deal([t]) :: t
      def deal(cards) do
        # not implement yet
      end
    end
    

    比較有用/有趣的內建 type

    數字的話有 neg_integer()non_neg_integer()pos_integer() 可以用。

    高階函式可以用 (type1 -> type2) 等箭號系列當做參數或是回傳值的型別標注。

    nonempty_list 表示不為空的串列,跟 maybe_improper_list,用來表示中間階段,其結尾還不是 [] 的 list,那麼就有延伸的 nonempty_improper_listnonempty_maybe_improper_list

    在 map 的部份可以做出非常細緻的定義,需要有什麼 key,對應的值的型別是什麼,還有可以有 optional 的選項。這些就請看文件了。

    mfa 代表 {module, funciton_name, arity} 的 tuple,這個在做 meta-programming 的時候蠻好用的。

    其它的標註屬性

    @typep 用來表示這個自定義的型別只在目前的 module 中可見,而 @opaque 則是外界看得到這個型別,但是無法知道裡面的結構。

    @callback@macrocallback 則是在操作 Behaviour 的時候使用的,常見的情況就是在實作 GenServer 或其它的 Behaviour 時,標註 @callback 就會幫你檢查是否所需要的 callback 都有妥善的依規格實作。

    Success typing 是什麼

    我們來寫一個 compare 的例子,給兩張牌,回傳第一張跟第二張相比的大小。

    @spec compare(Card, Card) :: :gt | :eq | :lt
    def compare(_c1, _c2) do
      "opps"
    end
    

    在無視輸入直接回傳一個錯誤的型別, 如果你的編輯器有裝 language server protocol,那麼就會跳出有問題的提示:

    Wrong spec

    或是你也可以在 project 中安裝 dialyxir,這是 Erlang 內建的型別檢查工具 dialyzer (唸 di-a-lai-zer) 的 wrapper,按其說明 compile 並執行,就會開心的看到如下的錯誤訊息:

    Dialyzer error

    那麼把型別改成正確的回傳就沒問題了:

    Type correct

    那如果不是固定的值,而是計算的結果呢?回傳 1 + 1,一樣可以看到型別錯誤的提示。

    Calc type error

    不過我們高興的太早了。如果我們這樣寫的話, success typing 會認為這段程式碼是 合法的

    wat

    原因是因為雖然"我們"知道這段程式碼只會回傳 “wat” 字串,但是 Elixir 不知道。在型別檢查階段,只能確認 Enum.random/1 函式的回傳型別是 any(),所以 dialyzer 覺得呼叫 Enum.random/1 回傳 :eq | :gt | :lt 其中一個的機率不為零,所以就放行了…。這段程式碼只接收了 list of string 的這個事實,要到編譯階段才會知道,但那時型別標注都已經被移掉了。

    看到這裡應該就可以理解為什麼 Elixir / Erlang dev 一直以來沒有非常認真的看待 type 的原因了。

    還有一些其它的

    不過只要是會長期維護的專案,我個人的習慣還是都會加上 dialyxir 並設到 CI 裡。經驗上我還是有好幾次在修改了程式碼後,unit test 通過但是型別檢查噴了錯誤訊息救到我的案例出現。

    而在社群的關注下, José Valim 在 ElixirConf EU 2022 講了很久的 typing 的議題,也宣布了有想要朝 gradual typing 進行認真的研究的方向。在上個月也在官網發了文章把 talk 的內容匯整起來:

    https://elixir-lang.org/blog/2022/10/05/my-future-with-elixir-set-theoretic-types/

    不過官方目前的態度是不知道這個研究會產生什麼結果,所以也只能等著看看了…

  • a(nother) micro step

    …yes.

  • Elixir.tw online meetup 指南

    Meetup 形式與主題

    每次預計 45min ~ 1hr 主題 talks, 另外有 30min 左右的發問與閒聊時間。而每次聚會的talks 部份會測錄並上線。

    • 2020/04/14 Elixir 基本語法
    • 2020/05/12 Phoenix

    其它主題(日期與形式規劃中)

    • 進階語法
    • macro
    • Phoenix LiveView & Channel
    • Concurrent, OTP & Flow
    • Ecto
    • ETS
    • Nerves
    • deployment
    • rustler

    事前準備

    線上 meetup 將使用 Google Hangout Meeting,並用 Visual Studio Code 的 LiveShare 功能輔助。另外我們建議在你的電腦上安裝好 Elixir 的開發環境。

    教材

    若你對 Elixir 語法還不熟悉,建議你可以用 Elixir School 的教學做為參考。第一次講解的內容基本上涵蓋該教材基礎部份的 1 ~ 7 章。

    其它

    若你有任何想要分享的主題或是特別感興趣的方向,歡迎在 Facebook 粉絲團,Slack,或來信 ask@elixir.tw 與我們討論。


    Elixir 能用來做什麼,好處在哪?

    誰在用 Elixr (或它的底層: Erlang)

    Elixir:

    • Discord
    • Pinterest
    • Moz
    • Toyota Connected
    • Grinder
    • 怪物彈珠
    • 任天堂

    Erlang

    • Ericsson
    • WhatsApp
    • RabbitMQ
    • Riak
  • Steam 上的程式教學類遊戲

    農曆年期間比較有空,玩了一些之前買的遊戲。這次特別試了幾個標榜讓不會寫程式的人學寫程式的遊戲。分享一下試玩的心得。

    1. 7 Billion Humans

    考慮到劇情的話我最喜歡的是 7 Billion Humans。它用拖拉語法的方式下指令,一開始還蠻好上手的,但是因為只有 goto 那樣的結構,而操作的時候又是一次對所有的 worker 下指令,所以常常要想一下執行後每個人運作的順序。但是介面有正體中文,以「想要體驗一下寫程式大概是怎麼一回事」來說還蠻適合的。

    2. while True: learn()

    這個遊戲說的是某個 programmer 發現他的貓會寫程式,所以要開發一個喵語翻譯器。想要模擬的是機器學習的內容,所以是試著組合各種過濾器(神經網路?)。因為精度高的過濾器慢加上有時間限制,所以習慣找最簡單的組合的想法常常會超時,手感跟 deterministic algorithm 差很多。也有正體中文介面不過有些翻譯怪怪的。

    3. Grey hack

    這個遊戲打開就是一付 Linux 樣,有瀏覽器,郵件軟體跟終端機,然後就要去接任務 hack 東西,也可以寫 script。基本上沒什麼「我在玩遊戲」的感覺,往好處來說就是 context switch 成本很小? (不是

    不過我還蠻喜歡的。

    4. Screeps

    Screeps 就要真的寫程式碼了。這個遊戲用 JavaScript 操作,玩類似 starcraft 般的遊戲。所以對程式完全陌生的人應該會蠻辛苦了。一開始的 tutorial 蠻簡單,但是每次都帶上文件連結,「我還是在寫程式啊」的感覺比上個遊戲更強烈。還好我就是愛寫程式。愛就是愛。 XD


    其它看起來比較有趣但是還沒有買的有 InfinifactoryOpus MagnumNeon Noodles

  • Let's (re)start from here.

    最近的時間大半都花在這上面了。

    算算應該是第五次弄部落格系統。算一下扣除上古時期用現成的之外,每個系統平均各寫六篇文章,也都撐不過兩年。前幾個分別用了 Refinery CMS -> jekyll -> middleman -> jekyll。想來架系統的總時數應該超過寫文章的時間 XD

    而這次用上了 Gatsby + tailwindcss,除了恢復一下 GraphQL 的手感之外,這次還挑戰了不套別人做的版型,自己把類似上一個部落格的 style 刻出來。想說來分享一下這些技術的感想:

    Gatsby

    雖然說是個 static site genertor,生成頁面的部份靠 React 來處理,開發時會啟動一個 nodejs server,將網站 meta-data 及檔案內容做成 GraphQL 的 endpoint,因此每個頁面都能簡單的拿到想要的資料。想要多拿欄位或是自訂欄位、變更取值的條件,甚至做 pagination 時,都是靠 GraphQL 段來處理的,因此改起來相當容易,graphiql 開起來就知道有什麼東西可以拿了,不必像 jekyll 那樣一直去翻 plugins 的文件。

    Gatsby 本身的文件也蠻完整的,雖然沒有整理的非常好,看起來相對瑣碎一點,不過大部份想要的功能都找得到範例。像是 RSS、syntax hightligh 很快就能裝起來。 Mathjax 也沒有什麼難度,不過因為 mdx (React component in markdown) 的關係,語法有時候會壞掉,再看情況要不要換成 katex 好了。

    $\displaystyle \mathcal P \mathcal A \mathcal 1: 1\ \in \mathbb N \\\\ \mathcal P \mathcal A \mathcal 2: \forall\ n\in\mathbb N\ [\ n'\in \mathbb N\ ] \\\\ \mathcal P \mathcal A \mathcal 3: \forall\ n\in \mathbb N \ [\ n' \neq 1\ ] \\\\ \mathcal P \mathcal A \mathcal 4: \forall\ m \in \mathbb N\ \forall\ n \in \mathbb N\;[\ m' = n'\Rightarrow m = n\ ] \\\\ \mathcal P \mathcal A \mathcal 5: (P(1) \land \forall\ k \in \mathbb N \; [\ P(k) \Rightarrow \ P(k')])\ \Rightarrow \forall\ n \in \mathbb N \; [\ P(n)\ ] $

    如果想試試 React + GraphQL 的話,我覺得 Gatsby 是不錯的上手點。

    tailwindcss

    這個是被龍哥推坑的。認真玩了一下,發現 inline class style 跟 component style JS (React, Vue) 根本是天作之合。寫起來幾乎沒有在考慮樣式互相影響蓋來蓋去,應該要怎麼調整結構隸屬的問題。一般的專案由於打出來的 css 會很大包,需要用到 purgecss 去清掉沒用到的樣式。而在 Gatsby 裡這段它在生成靜態頁面的時候就會幫你處理掉了。

    之前看 tailwindcss 教學的時候發現作者用了 Sizzy, 試用起來發現在調 RWD 的時候蠻好用的,不過軟體不太穩定,時不時會反應很慢,有些設定按鈕按下去一定會當機 XD 再觀察一陣子好了。

    Migration

    由於 jekyll 也是用 markdown,所以寫個 python script 轉一下舊文章的檔案結構,手動清一下語法,花的時間比想像中少很多。

    Deploy

    由於有人做好了 deploy 到 AWS s3 的 library,懶得重做就先搬到 AWS 了。希望不會多花太多錢 XD


    希望這樣會比較有動力寫文章。 XD

  • 安裝 Elixir 環境

    本文介紹幾種在電腦上安裝 Elixir / Erlang 環境的方法。

    Mac

    A. 使用 asdf (推薦)

    asdf 是類 unix 作業系統上類似 rvm, rbenv 或 nvm 的語言版本管理套件。特別之處在於它可以安裝不同的 plugin 來管理多種不同的語言

    pre request

    • homebrew
    • git

    Install

    1. 安裝 asdf 及 erlang 需要的元件
    $ brew install \
      coreutils automake autoconf openssl \
      libyaml readline libxslt libtool unixodbc \
      unzip curl wxmac asdf
    
    1. 將 asdf init script 加到 zshrc 中,若你用的是 bash 或 fish 請參考官方說明這步之後需要重啟 shell
    $ echo -e '\n. /opt/homebrew/opt/asdf/libexec/asdf.sh' >> ~/.zshrc
    $ echo -e '\n. /opt/homebrew/share/zsh/site-functions' >> ~/.zshrc
    
    1. 安裝 asdf 的 elixir 及 erlang plugin
    $ asdf plugin-add erlang
    $ asdf plugin-add elixir
    
    1. 用 asdf 安裝最新版的 Erlang。這一步一般來說需要很久,可以去聽個兩首歌再回來。
    asdf install erlang latest
    
    1. 用 asdf 安裝最新版的 Elixir。由於 Elixir 是預編譯版本,所以可以選擇用符合自己 Erlang 版本編譯的版本。
    asdf install latest elixir
    

    Postgresql 及 NodeJs

    如果想試試 phoenix,那麼需要安裝 postgresql 及 nodejs 5.5 以上的版本。這裡一樣使用 asdf 來安裝 nodejs,若你的系統中已存在其它 nodejs 版本,可以省略後面兩步。

    # 1. 安裝 postgresql
    $ brew install postgresql
    
    # 2. 安裝 asdf 的 nodejs plugin
    $ brew install gpg
    $ asdf plugin-add nodejs
    
    
    # 3. 用 asdf 安裝 nodejs
    $ asdf install nodejs lts
    

    完工

    試一下會不會動:

    $ elixir -v
    $ which erl
    
    ## 如果有裝 phoenix 的話
    $ mix phx.new --version
    

    B. 使用 homebrew

    由於這個方法會跟著 homebrew upgrade 一起更新版本,所以比較適合下載來玩一下的情況。長時間的正式專案開發可能會遇到一些雷。

    $ brew install erlang elixir
    

    C. 使用 Docker

    1. 安裝 docker
    $ brew cask install docker
    
    1. 拉官方 image。
    $ docker pull elixir
    
    1. 跑起來試試看。按兩次 Ctrl+C 結束
    $ docker run -it --rm elixir iex
    
    1. 執行本機上的 elixir 檔案
    $ docker run -it --rm -v $(pwd):/tmp elixir elixir /tmp/my_elixir_file.ex
    

    Ubuntu/Debian

    A. 使用 asdf

    1. 安裝 asdf 及 erlang 需要的元件
    $ apt-get -y install build-essential autoconf m4 \
    libncurses5-dev libwxgtk3.0-dev libgl1-mesa-dev libglu1-mesa-dev \
    libpng-dev libssh-dev unixodbc-dev
    

    接著參考 Mac 的 2 ~ 6 步


    CentOS 7

    wget --no-verbose -P /tmp https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    
    yum install -q -y /tmp/epel-release-latest-7.noarch.rpm
    
    yum update -y -q
    
    yum upgrade -y -q --enablerepo=epel
    
    yum install -y -q wget curl unzip make git
    
    yum install -y -q automake autoconf readline-devel ncurses-devel openssl-devel libyaml-devel libxslt-devel libffi-devel libtool unixODBC-devel
    
    groupinstall -y 'Development Tools' 'C Development Tools and Libraries'
    
    yum install -y -q wxGTK3-devel wxBase3 openssl-devel libxslt \
        java-1.8.0-openjdk-devel libiodbc unixODBC erlang-odbc
    
    yum install -y -q install gpg perl perl-Digest-SHA
    

    接著參考 Mac 的 2 ~ 6 步


    Windows

    A. 使用 Linux Sub System (推薦)

    1. 參考微軟官方說明,安裝 Linux 子系統。若不知道要用什麼的話,那就選 Ubuntu 吧。

    2. 啟動 Linux 子系統後,照著 Ubuntu/Debian 小節操作。

    B. 使用 scoop

    參考 Scoop 官網說明

    1. 用系統管理者權限打開 powershell

    2. 貼上以下指令並按 [Enter] 執行

    iwr -useb get.scoop.sh | iex
    
    1. 安裝 elixir
    scoop install elixir
    
    1. 執行看看
    iex.bat
    

    C. 使用 chocolatey

    參考 chocolatey 官網說明

    1. 用系統管理者權限打開 powershell

    2. 貼上以下指令並按 [Enter] 執行 (從官網 copy 會比較方便)

    Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
    
    1. 安裝 elixir
    choco install elixir
    
    1. 執行看看
    iex.bat
    

    D. 使用 Docker

    同 Mac -> C. 使用 Docker

  • Phoenix LiveView 概念篇

    在 2018 九月 ElixirConf 的 ending keynote 中,Phoenix 的作者 Chris McCord 發表了正在開發中的新套件,Phoenix LiveView。而上週五 (3 月 15 日) 這個套件終於在 GitHub 上公開了。 本篇將介紹 Phoenix LiveView 的想解決的問題、基本概念,以及一些個人的想法。

    簡單場景:Server side render

    在介紹 Phoenix LiveView 之前,先來回頭看一個用 server side render 寫出來的表單輸入場景:註冊帳號。首先使用者輸入 username 及 email,按下送出。發現註冊失敗,原來電話號碼是必填欄位。

    接著亂填一個電話號碼,按下送出,錯誤訊息提示電話號碼格式不對。

    把電話號碼改好後,按下送出,才發現這個 email 已經被註冊過了。

    換了一個 email 按下送出,這次才終於註冊成功。只有三個欄位的表單,使用者按下了四次送出鈕才完成。

    下一步:用 JavaScript 改進

    我們想要的是當按下鍵盤按鍵時,就幫我們判斷格式是否正確,欄位是否有填完等等。為了改善這糟糕的互動體驗,主流的做法是引進 JavaScript 。通常會先以原生的 JavaScript ,或許再配上一點 jQuery 用 AJAX 來處理,接著依場景在前端實作將各種欄位的驗證及錯誤提示,當然後端的驗證還是要保留著。不然就會發生前沒多久某屈姓藥妝店的新聞了。

    但隨著功能變多,沒有仔細規劃的話,我們的網頁就逐漸變成了一鍋「事件湯」。各個 listen events 間有錯綜複雜的觸發順序與依賴關係,一不小心就會讓該發生的事沒觸發到,或是不該發生的事件觸發了。

    這時就會開始考慮使用 JavaScript 框架。但撇開漫長的工具棧選擇及組織社會性問題,各個頁面流程都得逐漸遷移到前端去。再來就發現頁面的 SEO 沒了,如果很在乎 SEO 的話,那就得在中間做一層 isomorphic layer,讓爬蟲也能爬到資料…

    即使天時地利人和,總算把整個站改成了 Single Page Application 加上原本的後端的 Server。此時後端的 Server 已經變成一個純 API Server 了,那麼就會開始懷疑後端 Server 的實作方式是否符合目前架構的特性。舉例來說,為什麼要用 Rails 做純 API Server 呢?是不是改成 Sinatra ,甚至用 Golang 會比較好?

    Phoenix LiveView

    在許多情況下,我們只是想要一些些比較好的使用者互動而己。

    可不可以沿用後端的頁面流程及驗證邏輯,卻又能即時的跟使用者互動呢? Phoenix LiveView 就是在這個概念下產生的一種解法。

    Chris McCord 在 announcement post 裡是這麼說的:

    Phoenix LiveView is an exciting new library which enables rich, real-time user experiences with server-rendered HTML. LiveView powered applications are stateful on the server with bidrectional communication via WebSockets, offering a vastly simplified programming model compared to JavaScript alternatives.

    Phoenix LiveView 讓你可以在 HTML tag 上用 phx- 屬性註明綁定的事件,但不是由前端進行處理,而是在事件觸發時,透過 websocket 將資料傳到後端,處理完成時後端主動將資料推至前端進行部份渲染。這麼一來,我們的網頁就有了保持狀態的能力,也就是上面引言中 “Stateful” 的意思。

    在下圖的例子中,我們用 phx-click="inc"幫 + 這個按鈕綁上 click 事件。並在後端用 handle_event/3 處理接收到的 inc 事件資料。這樣一來每次按下這個按鈕,就會觸發事件,用 websocket 傳送資料到後端。處理完成後,一樣用 websocket 將資料傳回前端重新渲染 。由於 handle_event/3 是在 server 端處理的,所以這邊的程式可以直接呼叫原本的流程及驗證邏輯等既有程式。

    Phoenix Channel 效能

    José Valim 跟 Chris McCord 都說過他們開發這個語言/框架的最主要原因,就是平行化處理。因此 Phoenix 自專案開始就內建了 Channel 這個處理 WebSocket 協定的模組,在建立連線後,Server 端除了被動的接收從 Client 來的訊息之外,也可以主動推送資料到 Client 端。適用在聊天室等 Cllient 端需要知道 Server 端的連續狀態變化等場景。

    得益於 Elixir / Erlang 優異的平行處理能力,Phoenix Channel 有在 55,000 使用者同時連線 websocket 的情況下,廣播訊息至 200 人的聊天室裡平均 0.24 秒的記錄。建構於其上的 Phoenix LiveView 甚至在官方 demo 裡放了一個 server side rendering 的動畫範例 rainbow ,純靠 server side 不斷的將更新的 div 推送到前端製造動畫效果。

    在我的電腦 (MacBook pro 15" 2015) 上,在不開 development tools 的情況下,60 fps 相當順暢,超過 85 fps 就會偶爾會出現卡頓感了。

    錯誤處理

    除了平行處理的能力之外,Erlang 的另一個重要特性就是容錯能力 (fault tolerance)。當 phoenix channel 在 server 端發生執行期錯誤、或是接收到不存在的事件時,設計上會使得處理該 channel 的 process 陣亡,並由 supervisor tree 生成另一個新的 channel 與 client side 對接。

    從使用者的角度來看,當發生錯誤時,瀏覽器會短暫停頓(這時顯示的是 websocket 未連線的 fallback 畫面),接著就回復初始的狀態。

    適合場景

    在 Elixir forum 的討論裡,Chris McCord 指出 LiveView 已知適合用在下列情況中:

    • 應用程式裡需要大量使用者互動的地方,如提示訊息、非同步工作狀態顯示、進度條、儀表板、附掛小工具等
    • 表單互動。如驗證、會依不同選項變動的動態表單、設定精靈等
    • 會需要即時知道 server 端狀態的東西
    • 需要 server 參與的使用者互動,如搜尋、自動補完等

    在官方 Example 裡還放上了 LiveView 做出來的貪食蛇及 PACMAN 遊戲。在討論裡 Chris McCord 也說了這樣的話:「我們的計劃是先從小的地方開始,看看我們大家會用這個做出什麼東西來(,再決定之後的方向)。」

    個人想法

    Client side rendering 在這五六年蓬勃發展,也有它無可取代的應用場景。例如 Gmail、Netflix 等等。另外 server 端只做 API,而由不同樣態的客戶端如瀏覽器、手機 App 分別與之對接也是在規模變大時很常見的做法。

    但當 Phoenix LiveView 的出現帶來了另一種輕量級的可能性時,將應用程式改成 client side rendering 的決策壓力線將會向右推遲。而在與其它框架比較時,Phoenix 會在 websocket server 的候選清單中取得更為優勢的地位。

    順帶一提目前已經有人把 Phoenix LiveView 跟 Vue Component 搭在一起用 ,依這個思路與 React 或 Web Component 合併看來也完全可行,只是除了在遷移的過渡情況之外,還想不到能這樣能拿來幹麼就是了。

    下一篇會導覽 Phoenix LiveView 的官方範例程式碼,敬請期待了。 Happy hacking!

    References

    ElixirConf 2018 Ending Keynote - Chris McCord

    Phoenix LiveView Repo

    Phoenix LiveView Examples

    Phoenix LiveView blogpost

    Phoenix Channel vs Rails ActionCable

  • Ruby 2.6 的新功能

    Ruby 2.6 除了主打的 JIT 之外,還引進了一些有趣的新功能。其中有一些可以讓 Ruby 在函數式風格的寫法上更加自然流暢。想來稍微展示一下這些功能的用法。

    1. Compose operator:
    Proc.>>Proc.<<

    雖然 Ruby 天生在某些用法上就採用了函數式風格(例如沒有 for 迴圈,而是用 each + block),但由於高階函式接受的是 block 而非 lambda,lambda 本身也無法簡單組合起來。所以一直以來匿名函式在 Ruby 中除了拿來當參數傳遞之外,用法相當受限。

    在正統的函數式編程中,用 function composition 的方式將多個小函式組合在一起是很基本的操作,舉個之前的例子,假設你想要這樣的連續調用:

    # 注意,因為假設這些函數都是 lambda,所以要用 `.()` 調用
    request = generate_request.()
    response = get_response.(request)
    body = parse_body.(response)
    html = render.(body)
    

    其實除了 html 這個最終結果外,我們不需要中間的臨時變數,而可以改寫成這樣(然後被同事記恨):

    html = render.(parse_body.(get_response.(generate_request.())))
    

    Ruby 2.6 開始你可以用 << (compose) 或是 >> (pipe) 來組合 Proc (或 lambda)。假設我們有兩個 lambda fg,這兩個運算子的作用如下:

    (f >> g).(x)
    # 等同於
    g(f(x))
    
    
    (f << g).(x)
    # 等同於
    f(g(x))
    

    我自己的記憶方式是把這兩個 operator 看成函式呼叫流程的箭頭。f >> g 就是先調用 f ,再將結果傳進 g。反之 f << g 則是先調用 g 再傳給 f

    那麼之前的例子就可以改成下列兩者之一:

    get_html = generate_request >> get_response >> parse_body >> render
    html = get_html.()
    
    # 或是
    
    get_html = render << parse_body << get_response << generate_request
    html = get_html.()
    

    Ruby 2.6 的文件範例如下:

    f = proc {|x| x * x}
    g = proc {|x| x + x}
    
    (f << g).call(2) # => 16
    (f >> g).call(2) # => 8
    

    再進階一點配合 Ruby 原本就有的 Object#methodMethod#curry,想來可以組合出非常有意思的寫法。

    2. Enumerable#filter

    函數式編程最著名的三個高階函式就屬 mapreducefilter 了。但在 Ruby 中,filter 這個高階函式被改名為 select,這一版加上了 filter 的別名,跟其它語言的慣用法一致,再也不會等到測試爆掉才想起函式名稱不一樣了。

    [1, 2, 3, 4].filter {|i| i % 2 == 0} # => [2, 4]
    

    3. Endless range

    現在範圍運算子 .. 可以不加結束的參數:

    [:a, :b, :c, :d][2..] # => [:b, :c, :d]
    
    
    (:b..).lazy.zip(10..).first(3) # => [[:b, 10], [:c, 11], [:d, 12]]
    

    4. Array#union and Array#difference

    Array#union 是聯集,Array#difference 是差集。直接上範例:

    [1].union([2, 3], [1, 2, 4, 5]) # => [1, 2, 3, 4, 5]
    
    
    [1, 1, 2, 2, 3, 3, 4, 5].difference([1, 2, 4]) # => [3, 3, 5]
    

    結語

    除了這些之外,Proc.call 調用快了大約 1.4 倍。另外還有 Object#thenHash#mergeEnumerable#to_h 等等,就找機會試玩看看吧。Happy hacking!

  • Elixir 的並行機制:基礎部份

    前陣子有幸可以亂入 Functional Thursday 介紹 Elixir 及 Erlang 。Q&A 時穆老師問了個「有沒有 lock 機制」的問題,覺得那時沒有回答好,想說寫篇文章來說明。也希望藉此機會,試著逐篇記錄一下我目前對 Erlang /Elixir 中並行機制的理解。

    先說結論,Actor model 在應用層不需要鎖,因為不論讀寫,訊息是阻塞並依序處理的

    Actor model 概念

    Erlang / Elixir 裡的並行機制是俗稱的 Actor model。在整個運行的系統裡,每個獨立執行的單位稱之為 Actor。各個 Actor 間不與其它人共享記憶體,而是透過互相傳遞訊息來得知其它 Actor 所持有的資訊。就像是一個房間裡有很多人,每個人都各自做自己的事,如果有某個人想知道另一個人的知識,那就要主動開口問他並等待回覆。

    在 Erlang / Elixir 裡,整個運行系統 (BEAM 虛擬機) 裡,可以產生多個 light-weight process。這個 process 並非作業系統的 process,而是啟動耗時 1~3 µs 的輕量虛擬機 process。如果你寫過物件導向的語言,可以把它想像成類似 object instance 的東西。

    前情提要:底層通訊機制 spawn/1send/2

    首先我們可以用 spawn/1 來生成一個 light-weight process (下稱 process),傳入的參數是一個函數。spawn/1 的回傳值則是生成的 process 的 pid。而 process 間的溝通,則是用 send/2 帶上 pid 及要傳送的訊息。

    例如我們可以讓新的 process 進行計算後,將結果傳給自己。由於 iex ( Elixir 的 repl ) 也是作為一個 process 啟動的,所以用 self/0 也可以拿到它的 pid,我們用它來試試訊息傳遞是怎麼一回事。

    註:依 Elixir 慣例,spawn/1 代表名為 spawn, 接收一個參數的函式。其它依此類推。

    current_pid = self() # 拿到目前的 `pid`
    
    pid =
      spawn(fn ->
        result = 1 + 1 # 做一些複雜的計算
        send(current_pid, result)
      end)
    
    flush() # 將收到的訊息全部沖出來看 (iex 限定函式)
    

    上例的圖示如下,我們在 iex 中,用 self/0 找到自己的 pid 為 0.101.0,用 spawn/1 生成一個新的 process,讓它在計算完成後,將結果用訊息送回 0.101.0。

    到此為止是上次 meetup 時分享的內容。

    receive/1 檢查信箱

    每個 actor (也就是 process) 都分別有一個不與其它人共用的信箱,依傳入時序存放未處理的訊息。讀取訊息時,則是依 FIFO 的順序取出。大概像是這樣:

    在 process 中要處理訊息時,會用 receive/1 來對收到的訊息進行 pattern matching。要注意的是一旦進入 receive/1 區塊,該 process 會阻塞並開始檢查信箱,直到比對到一筆符合的訊息,就調用該子句進行處理。

    current_pid = self() # 拿到目前 (iex) 的 `pid`
    
    pid =
      spawn(fn ->
        receive do
          {caller, i} -> send(caller, i + 1)
        end
      end)
    
    ## 在 iex 裡操作生出來的 process
    send(pid, {current_pid, 100})
    Process.alve?(pid) # => process 死掉了 QoQ
    flush() # => iex 收到 101 這條訊息
    

    在上面我們看到 process 處理完訊息之後就陣亡了。那是因為每個 receive/1 只處理一條訊息,就結束區塊阻塞,往下執行。而 spawn/1 在函式調用結束後,就會終止該 process 了。

    持續活著的 process

    在上例中 receive/1 只執行(阻塞)一次,所以 process 在發送完訊息後生命週期就結束了。所以我們需要有個辦法讓它保持活著的狀態。我們先把參數用到的函式寫成具名函式,並將 receive/1 的區塊抽出來,這樣我們就可以遞迴的呼叫它。這麼一來這個 process 就能一直活著了。

    defmodule PingPong do
      def start do
        spawn(&loop/0) # 生成 process 並回傳
      end
    
      def loop do
        receive do
          {caller, :ping} -> send(caller, :pong)
          {caller, :pong} -> send(caller, :blah)
          :kabom -> exit(:normal) # 結束 process
        end
    
        loop() # 用遞迴讓這個 process 活下去
      end
    end
    
    ## 在 iex 裡操作生出來的 process
    pid = PingPong.start
    send(pid, {self(), :ping})
    Process.alive?(pid) # => true
    
    send(pid, {self(), :pong})
    Process.alive?(pid) # => true
    
    send(pid, :kabom)
    Process.alive?(pid) # => false
    
    flush() # 可以看到回傳的兩條訊息
    

    在上面的例子裡,當 receive/1 處理完一條訊息之後,我們就遞迴的呼叫 loop/0 ,這樣就會啟動新一輪的 receive/1 來處理下一條訊息。我們可以看到在送出 :kabom 之前,該 process 一直是活著的。

    帶著狀態的 process

    在 BEAM 虛擬機裡,process 有自己的 heap 及 stack,不與其它人共享,再加上 Erlang / Elixir 的值是 immutable 的,我們便可以讓 process 運行時記住一組資料,慣例上稱之為 process 的 state。需要讀取或是更新這個 state 的其它人,都只能用送訊息給這個 process 的方式讀寫。這個 process 即是這個狀態的 single source fo truth。聽說費波納契是函數式編程的 101, 那我們就來做一個 FibCounter:

    defmodule FibCounter do
      def start do
        init_state = [0, 1] # 起始的 state
        spawn(fn -> loop(init_state) end)
      end
    
      def loop([first, second] = state) do
        next_state =
          receive do
            :next ->
              [second, first + second] ## 更新狀態
            {:get, caller} ->
              send(caller, first) ## 讀取狀態
              state # 保持一樣的 state
            :reset ->
              [0, 1]
            :kabom ->
              exit(:normal)
          end
    
        loop(next_state)
      end
    end
    
    ## 在 iex 裡操作生出來的 process
    counter = FibCounter.start
    send(counter, :next)
    send(counter, {:get, self()})
    send(counter, :next)
    send(counter, :next)
    send(counter, :next)
    send(counter, :next)
    send(counter, {:get, self()})
    send(counter, :reset)
    send(counter, {:get, self()})
    flush # => 拿到三條訊息,1, 5 跟 0
    send(counter, :kabom)
    

    應用程式裡我們可以到處傳遞 counter 這個 process,任何人都可以對它發送 :next 或是 {:get, caller_pid} 等訊息。

    每次只處理一筆訊息

    當 process 進入 receive/1 區塊時,會取出信箱中第一筆(最早的)訊息,依序比對各個 pattern matching 子句。若成功比對就開始進行處理。若該訊息不符合任何 block,則擱置該訊息,並比對信箱中的下一筆訊息。若沒有比對到符合的訊息,則會阻塞到下一條訊息進來為止。因此不需要手動鎖定什麼東西,Actor model 就能保證發送訊息的人總是拿到最新的狀態 (對送訊時刻而言)。

    搶佔式調度

    從微觀上來看,在接收到訊息之後,每個 process 會一直處在忙碌狀態中。那如果有 process 需要工作很長的時間,佔著 CPU 資源該怎麼辦呢?這時候就要從巨觀層面來看 Erlang 著名的搶佔式調度了。Erlang 在虛擬機上預設會為每個 CPU 核心配發一個 Scheduler,它會啟動一條 thread,並決定哪個 light-weight process 可以使用目前的 CPU 時間。scheduler 會為每個 process 計算函式調用的次數 (reduction) ,超過了使用次數,就會強制切換給另一個 process 工作,之後切換回來時再繼續未完成的部份。概略來說切換的頻率大約是數微秒一次。

    這麼一來,就算系統中有 process 需要長時間的運算,也能保證其它的 process 依然可以取得 CPU 資源,將工作先執行完。

    垃圾訊息

    由於沒有比對到的訊息會被留在信箱裡,所以如果在信箱中堆積太多訊息時,會造成效能上的損耗。除了在環境參數或 process option 裡設定 max_heap_size 之外,你也可以在 receive/1 區塊中用子句來定期清理無效的訊息。

    OTP

    在實務上,Erlang / Elixir 較少直接使用 spawn/0send/2 來產生 process 及傳遞訊息。而是使用 Erlang 包裝好的 OTP (open telecom platform) 工具,例如 GenServerSupervisor 來建構系統。這部份就是下一篇的內容了 (如果有的話)。

    許願

    話說希望之後有機會可以再去 Functional Thursday 發表Haskell 相關的主題。 XD

  • Beyond Caps lock

    應該不只一次跟別人提起:「如果這幾年用 mac 有學到些什麼的話,最重要的一件事肯定是把 Caps lock 改成 Ctrl」。是的,Caps lock 這麼重要的位置,居然放了這麼無用的鍵。把它換成 Ctrl 會讓你的生活品質大大提升。

    但是最近跟其它人推廣這個小技巧時,有時會得到這樣的反應:「但是我都用 Caps lock 來切換輸入法」。顯然 macOS 有想要改進這個無用的鍵,讓它多發揮一些功能。macOS 目前的設計是,如果你快速的按下 Caps lock 放開,那麼就會觸發切換輸入法的行為。如果你按久一點,那就是原先的 Caps lock。

    讓我們來從行為模式來思考一下幾個情況:

    1. Ctrl 是個綴詞鍵(或稱裝飾鍵),當我們按下 Ctrl 時,必然是配合另一個鍵才會觸發我們想要的行為。
    2. 切換輸入法則是一個鍵就可以觸發的行為。

    所以如果我們可以做到這樣的事就完美了:

    1. 當快速按下 Caps lock 並放開,觸發切換輸入法
    2. 如果按著 Caps lock,並按下另一顆鍵,則視為 Ctrl + key

    是的,這是辦得到的。但需要 Karabiner 這個免費軟體,並稍加修改。


    1. 首先到 [https://pqrs.org/osx/karabiner/index.html](https://pqrs.org/osx/karabiner/index.html) 下載並依指示安裝,執行後會看到底下的畫面(記得要到**系統偏好設定 > 安全性與隱私權**打開相應的權限):

    1. 接著切換到 [Complex Modifications],並按下左下角的 [Add rule]

    1. 按下上方的 [import more rules from the Internet]

    1. 這時瀏覽器會開啟,列入可以匯入的各種規則,找到 Modify Keys 區塊下的 Change caps_lock key,按下右邊的 [Import]

    1. 回到 Karabiner 視窗,就可以看到多出一些可用的規則。找到 Change caps_lock to control if pressed with other keys, to escape if pressed alone.,按下右邊的 [Enable]。這個設定會在單獨按下時觸發 [ESC],而跟其它鍵一起按的時候變成 [Ctrl + key]

    1. 雖然上面的設定也很不錯,但是我還是想要切換輸入法。這就要動用到編輯器了,用你喜歡的編輯器打開 “~/.config/karabiner/karabiner.json”,找到我們匯入的規則區塊,也就是寫著 “description”: “Change caps_lock to control…” 的那個 JSON object (花括號)。

    1. 將底下的 “to_if_alone” 裡的 key_code 從 “esc” 改成 “caps_lock”,為了避免誤解,也順便將上方說明改成 “…to caps_lock if press alone.”

    1. 要切換輸入法,記得要在 macOS 的系統偏好設定 > 鍵盤 > 輸入方式 ,將使用大寫鎖定鍵來切換… 核取方塊打勾

    Note: 這個方式在切換到 macOS 內建的輸入法,如注音或是雙拼等都運作的很好。而我個人慣用的嘸蝦米,則是第一次要用 [Ctrl + Space] 切換到嘸蝦米之後,按下 [Ctrl] 可以輸入英文,再按一次回到嘸蝦米。不過右上角的輸入法則始終都會顯示嘸蝦米。

    Note2: 我的系統有裝 Better touch tool,本來想用它來改,但目前似乎不提供這種功能。但 BTT 跟 Karabiner 一起運用目前都蠻正常的。

    Happy hacking!

  • List 操作小技巧

    最近寫 Elixir 時,有個慣用的 functional 小手法,順手記錄一下。

    1. 無差別包裝 List

    當處理的對象可能是 nillist,或是單個元素時,可以直接將它們包裝成單層的 list 進行後續操作。

    Elixir 提供了 List.wrap 讓你直接將 nil 及 單個元素包裝成 list。對於原本就是 list 的對象則直接回傳:

    # Elixir
    List.wrap(nil)    # => []
    List.wrap(1)      # => [1]
    List.wrap([2, 3]) # => [2, 3]
    

    值得一提的是包裝 nil 會回傳空的 list,這對稍後要介紹的連續技起了重要的作用。

    Ruby 則可以用 Array()

    # Ruby
    Array(nil)    # => []
    Array(1)      # => [1]
    Array([2, 3]) # => [2, 3]
    

    在 JavaScript 裡就得要自己拼了,以 Ramda 為例:

    let wrap = R.compose(R.filter(R.identity), R.unnest, R.of)
    
    wrap(null)    // => []
    wrap(1)       // => [1]
    wrap([2, 3])  // => [2, 3]
    

    這裡用 R.unnest 而非 R.flatten,是因為它不會把串列全部攤平至只剩一層。這個特性在你想要處理的「元素」本身就是一個陣列時會排上用場。

    2. 攤開 map 的結果。

    flat_map 可以將 map 的結果攤開到外層串列中。這個看範例會比較好懂:

    [1, 2, 3]
    |> Enum.flat_map(fn x -> [x, x] end)
    
    # => [1, 1, 2, 2, 3, 3]
    

    flat_map 是函數式語言/函式庫基本的操作之一,應該都找得到。Ramda 裡叫 R.chain

    3. 連續技:優雅的消失

    結合上面兩個函式,先把含有 nil 的集合 wrap 成一個個的串列,再用 flat_mapflatten 攤開。這個時候就會發現 nil 在操作過程中直接消失了,不需要 filter,更不用 if/else 判斷。例如:

    class = %{
      teacher: %{name: "John", age: 40},
      assistant: nil,
      students: [%{name: "Amber", age: 20}, %{name: "William", age: 22}]
    }
    
    class
    |> Map.values
    |> Enum.flat_map(&List.wrap/1)
    
    # => [%{age: 20, name: "Amber"}, %{age: 22, name: "William"}, %{age: 40, name: "John"}]
    

    實際應用時,通常會直接將上層集合傳到 flat_map 裡,並將處理的函式拿出來另外宣告,這樣可以用 pattern matching 對上層結構做更細緻的處理。

    Happy hacking!

  • Elixir 的 & 運算子

    在跟朋友討論 Elixir 的過程中,發現常會需要解釋 & 運算子的用法,決定寫篇完整的來科普一下。

    因為特殊符號很難 google 到正確的結果,& 在官方文件中的稱呼是 capture operator。它最主要的作用,就是補獲或是生成匿名函式。

    Eta conversion

    在 JavaScript 中,具名函式也是一級公民,可以直接傳遞。所以遇到在 lambda 中將接收到的參數原封不動傳給具名函式,並回傳其結果的情況下,可以直接傳遞具名函式。這在數學上叫 Eta conversion。講起來很拗口,看範例就很直覺:

    [-1, -2, 3].map(i => Math.abs(i))
    
    // 等同於
    
    [-1, -2, 3].map(Math.abs)
    

    但在 Elixir 中,具名函式不加括號視同零參數的呼叫,因此我們需要有辦法將具名函式轉換成 lambda。這就是 & 的第一個用法。在轉換其它 module 的函式 (正式名稱叫 remote function) 時,語法是 &Module.function/arity,記得斜線後要帶上參數的個數。

    Enum.map([:a, :b, :c], fn a -> Atom.to_string(a) end)
    
    # 等同於
    
    Enum.map([:a, :b, :c], &Atom.to_string/1)
    
    #=> ["a", "b", "c"]
    
    # 不能這樣寫
    Enum.map([:a, :b, :c], Atom.to_string)
    
    # 因為上一句會被解析成這樣:
    Enum.map([:a, :b, :c], Atom.to_string())
    

    當然轉換 local fucntion 或 imported function 也沒問題,不加 Module 名稱就可以。

    def double(i), do: i * 2
    
    Enum.map([1, 2, 3], &double/1)
    
    #=> [2, 4, 6]
    

    順帶一提,用 & 補獲/生成的函式不一定要寫在高階函式中,也可以另外指派給變數。由於被轉換成 lambda 了,所以呼叫時要用 .()

    f = &Kernel.is_atom/1
    
    f.(:atom) #=> true
    

    匿名函式

    Elixir/Erlang 裡,匿名函式的宣告比較冗長。因此遇到函式本體很短的情況下會覺得麻煩。

    Enum.map([1, 2, 3], fn i -> i * 2 end)
    

    這種情況就是 & 運算子派上用場的另一個地方,這也是最多人感到困惑的用法。

    Enum.map([1, 2, 3], &(&1 * 2))
    #=> [2, 4, 6]
    

    換句話說,fn i -> i * 2 end&(&1 * 2) 是一樣的意思。

    生成 List 或 Tuple

    若用 []{} 代替圓括號,呼叫後的結果會分別是 ListTuple

    l = &[&1, &2]
    l.(1, 2)
    #=> [1, 2]
    
    t = &{&1, &2}
    t.(1, 2)
    #=> {1, 2}
    

    更多參數及使用判準

    &1 是匿名函式接收到的第一個參數,多個參數也是可以的,就是 &2&3 遞增下去。

    fn = &(&1 + &2 + &3)
    
    fn.(1, 2, 3) #=> 6
    

    不過濫用 &() 語法的話,程式很容易就會變得難讀。個人的判準是內部超過 10 個字元,或是有三個以上的運算子,就寧願用 fn -> end 來宣告了。

    那麼要做出 Haskell 的 identity 就簡單了: &(&1)

    Enum.group_by(["a", "b", "c", "a", "b"], &(&1))
    
    # => %{"a" => ["a", "a"], "b" => ["b", "b"], "c" => ["c"]}
    

    Partial application

    綜合上面兩個語法,有種文件上沒寫清楚,很少人提,卻相當有用的用法。送了 PR 但還沒併進去。用 &1&Module.function 作出 partially applied 的 remote function:

    take_five = &Enum.take(&1, 5)
    take_five.(1..100)
    
    # => [1, 2, 3, 4, 5]
    

    local function 也是一樣的:

    first_elem = &elem(&1, 0)
    first_elem.({1, 2, 3})
    #=> 1
    

    大概就是這樣了。Happy hacking!

  • Pipe of JavaScript: _.chain()

    Update: 後來發現這個解法有一些缺點。目前大多用 Ramda 的 pipecompose 來處理。不然就等 tc39 上的 proposal 吧。


    不知道從什麼時候開始,愈來愈喜歡用 functional 的寫法來處理問題。在看完 JavaScript Allonge 之後更是一發不可收拾。上次的 Elixir 文章提到了愉快的 |> Pipe operator。一直想著怎麼 JavaScript 沒有類似的東西,但試過各種關鍵字就是找不到。

    直到前陣子因工作需要,認真的翻了 Lodash 文件,才發現真是踏破鐵鞋無覓處,那 function 就是接下來要介紹的 _.chain()_()。找了一下中文世界似乎沒有什麼說明,寫個小心得看看是不是真的只有我 Lag 了 XD

    先從問題開始,手邊有如下的資料結構:

    const poData =
      [
        { poNumber: 'FA1234-1', designName: 'A',,,, },
        { poNumber: 'FA1234-5', designName: 'B',,,, },
        { poNumber: 'FB2234-1', designName: 'C',,,, },
        { poNumber: 'FC3141-1', designName: 'D',,,, },
      ]
    

    想要拿到不重複的 poNumber 的前半段。

    ['FA1234', 'FB2234', 'FC3141']
    

    函數式的分解動作解法如下:

    const poNumbers     = _.map(poData, 'poNumber')
    const poNumberParts = _.map(poNumbers, num => num.split('-')[0])
    const result        = _.unique(poNumberParts)
    
    // => ['FA1234', 'FB2234', 'FC3141']
    

    但其實我們不需要中間那些臨時變數,所以可以寫成這種 Wirte only 風格來累積仇恨值:

    const result = _.unique(_.map(_.map(poData, 'poNumber'), num => num.split('-')[0]))
    

    縮排一下,看起來有點像 Lisp 了。但問題是執行順序是由內到外的,相當反直覺。

    const result = _.unique(
                      _.map(
                        _.map(poData, 'poNumber'),
                        num => num.split('-')[0]
                      )
                    )
    

    我們想要的,其實就是把函式執行的結果,當成呼叫下一個函數時的第一個參數。 而 Lodash 的 _.chain() 的功能正是如此:

    const result = _.chain(poData)
                    .map('poNumber')
                    .map(num => num.split('-')[0])
                    .unique()
                    .value()
    

    由於 _.chain() 是 lazy evaluation,所以要在最後方加上 .value() 才會開始調用。否則只會回傳一個未調用的嵌套函式。 Lazy evaluation 的好處是上例中的兩個 _.map 及一個 _.unique,在實際執行時陣列只會遍歷一次,效能可能會比原先的寫法好一點。

    另外 Lodash 還提供了 _(),如果在 chaining 過程中出現了 _.reduce()_.merge() 等等會處理成單一結果,或是會回傳 primitive value 的函式調用,就會自動 unwrap。

    const data = [
      {name: 'A', usdPrice: 10},
      {name: 'B', usdPrice: 20},
      {name: 'C', usdPrice: 30},
    ]
    
    const twdTotal = _(data)
                    .map('usdPrice')
                    .map(num => num * 31)
                    .reduce(_.add)
    
     // 呼叫了 _.reduce() ,所以不需要呼叫 _.value() 就會回傳數值
     // 這是個很假的範例不過你知道我意思。
    

    要注意雖然這個寫法看起來也是一路點下去,但是他的思路跟運作機制都與 jQuery 那種回傳 self 以繼續在同一個(變形過的) 物件上操作方法的 fluent style 是完全不同的。這種 chaining call 的運作方式是回傳一個用後方函式包裹前方函式的函式,讓你可以繼續包下去或調用。這概念及用法與 Rx.js 的 Observable 非常類似。

    另外 underscore 也有 _.chain(),沒有仔細看但是印象中用法雷同。


    • 即便是離開的資訊圈到南洋島國上做印刷,還是要處理傳產的 Excel 惡夢。反正沒有人要看 code,索性變本加厲的用上了 Rx.js。
    • 呼叫函式 => call the function,函式調用 => function invocation。兩者同義,依行文流暢擇用。
    • 最近繼續唸 Elixir/Erlang,OTP 好有趣啊。
  • 復活節特價 Ruby 書敗家指南

    @JuanitoFatas 今天分享了 紅寶石鐵道漫遊指南

    適逢本週感恩節特價,有人問起,就來快速列一些可以趁機入手的書。如果有閒的話,我再來補每本書的簡介。

    Disclaimer: 這是敗家清單,想清楚再往下捲。 我不會由此推薦清單獲得任何實質上的利益。 絕大部份的書我都買了,但我整本看完的只有一部份。 XD


    tl;dr: 就買 Confident RubyPractical Object-Oriented Design in Ruby: An Agile Primer 兩本。共約 37 USD。


    Pragmatic Bookshelf

    Discount Code: turkey2014 Discount: 50% for all ebooks End date: Dec 1, 2014

    1. Confident Ruby

    推薦指數: ☆☆☆☆☆ (必買)

    這本書列出數十種如何寫出漂亮乾淨的 Ruby code 手法及準則。在我心中地位如同 Kent Beck 實作模式的 Ruby 版。

    2. Exceptional Ruby: Master the Art of Handling Failure in Ruby

    推薦指數: ☆☆☆☆

    3. Metaprogramming Ruby 2: Program Like the Ruby Pros

    推薦指數: ☆☆☆

    我比較喜歡 Dave Thomas 這個 The Ruby Object Model and Metaprogramming 影片,但是似乎沒有特價。

    4. Crafting Rails 4 Applications

    推薦指數: ☆☆☆

    Elixir 語言作者寫的好書,分析 Rails 內部如何運作。喜歡 meta-programming 的話,可以從這本裡看到許多實際上的用法。當然也比較進階一點。

    5. Build Awesome Command-Line Applications in Ruby: Control Your Computer, Simplify Your Life

    推薦指數: ☆☆☆

    試試 Ruby 除了 Web 還可以幹啥的好起點。

    除此之外,Programming Ruby, Agile Web Development with Rails 4The RSpec Book 這種有一陣子的參考書就看個人想不想收了。

    PS: 跟 Ruby 不那麼有關的,我想偷推一下 Programming Elixir ♥♥♥♥♥

    另加三本 Working with 系列基礎書:


    InformIT

    Discount Code: BF2014 Discount: Buy 1 save 35%, 2 or more for 50% End date: 沒說,應該就到本週五吧?

    1. Practical Object-Oriented Design in Ruby: An Agile Primer

    推薦指數: ☆☆☆☆☆ (必買)

    Object oriented 其實可以很輕巧,很靈活,很愉快。不是一大堆 UML 跟繁雜的大設計 ( 我不會說是什麼語言給我們這種印象 )。這本書說明各種設計的問題,並介紹 OO 裡的準則,並用簡單的 Ruby 實現。直指 Smalltalk 精神的 OO 觀念佳作。如果你只看一本教 OO 的書,那就選它吧。

    2. Eloquent Ruby

    推薦指數: ☆☆☆☆

    3. Refactoring: Ruby Edition: Ruby Edition

    推薦指數: ☆☆☆☆

    如果你有 Java 版的話,那就不用多買。如果你看得懂 Java,那中文版比較便宜。 XD

    4. Effective Ruby: 48 Specific Ways to Write Better Ruby

    這本我買了還沒翻開過…

    Design Patterns in RubyRails 4 Way 就看個人需求了。


    no starch press

    Discount Code: GRAVYBOAT Discount: 50% for all ebooks End date: Nov 26, 2014

    1. Ruby Under a Microsope

    推薦指數: ☆☆☆☆

    這本在講 Ruby 內部的運作原理,算是理論書。我自己是蠻喜歡的啦…

    2. Rails Crash Course

    @JuanitoFatas 簡報裡有列,這本我沒看過也沒有買…

    Warning: 聽說 Book of Ruby 這本蠻雷的…

  • 淺嚐 Elixir

    來聊一下最近在唸的新玩具: Elixir。 發音是:「ㄦ利ㄎ洗爾」

    Why Elixir

    是說去年底 Peepcode 還活著的時候有出過一集 Elixir 的教學。我一直蠻仰慕這個寫出這本厲害的 Craft Rails Applications 的作者 José Valim ,但是這影片一直在 stack 的最底端沒機會去看。( 話說 José 是巴西人,回頭去看影片,一直會聽到小舌音的 “ㄟ赫爾”,要仔細聽前後文才發現是 “error”。)

    一直到上次 Dave Thomas 路過台灣被 Ruby Taiwan 社群拉來吃熱炒,席間聊到他寫的新書 Programming Elixir 。他說電腦跟手機核心愈來愈多,分散式運算是肯定是未來五年最重要的課題之一,也提到最後出線的也許不是 Elixir ,但是出頭的那個語言必然會有相似的概念 ( 看看 go 那個精美的 goroutine ) 。加上他總是說一年要學一門讓你重新思考的新語言,講的這麼有趣,一頓飯吃完腦也被順利的洗過一輪了。

    What is Elixir

    2012 才發表的 Elixir 是建在 Erlang Virtual Machine (BEAM) 上的函數式語言。繼承 Erlang 優秀的分散式運算模型 Actor model ,「目標是在 Erlang 生態系裡帶進高生產力及高擴展性的開發過程」。簡單來說就是像 Ruby 的漂亮語法,更一致的操作跟 meta-programming 能力。

    Elixir 這個字是萬靈藥的意思。Dave 的新書裡還開了這樣一個玩笑:“Of course, I’m not saying that Elixir is a magic potion (well, technically it is, but you know what I mean).”

    Disclaimer: Elixir 當下版本為 0.14.3。最近幾乎每個月都會有更新。雖然已有公司用在正式環境上,但幾乎都是原來就使用 Erlang 的公司才敢這麼做。另外我書也還沒整本看完。 XD


    我喜歡的部份

    0. Script and compiled

    Elixir 有兩種副檔名:exexs。執行 exs 副檔名時,預設會直譯並執行。若是 ex 副檔名,則會編譯成 BEAM binary 後執行。想要手動編譯 exs 檔案也是可以的。

    1. 樣式比對 (Pattern matching)

    Elixir ( 及 Erlang ) 的等號並不是單純的指派,而是樣式比對。當我們在數學上說 y = x + 1 時,並不是說把 y 指派成 x + 1。而是說當 x 為某值時,y 會依規則 ( 這裡指的是 + 1 ) 比對成另外一個值。

    在 Ruby 中,當你寫

    a, a, b = [1, 2, 3] # => a 會是 2
    

    因為第二次指派覆蓋了第一次指派。

    而在 Elixir 中,則是這樣:

    [a, a, b] = [1, 2, 3]
    # => ** (MatchError) no match of right hand side value: [1, 2, 3]
    
    [a, a, b] = [1, 1, 3]
    # => 因為能成功比對,a 會是 1,b 則是 3
    

    樣式比對在 Elixir 裡用法相當巧妙,例如:

    [ head | tail ] = [1, 2, 3, 4, 5]
    # =>  head 是 1, tail 是 [2, 3, 4, 5]
    

    以及函式呼叫:

    parse_response = fn
      {:ok, value}  -> render(value)
      {:err, msg} -> show_error(msg)
    end
    
    parse_response.(response)
    

    會依 response{:ok, something} 或是 {:err, something} 決定執行的方法區塊。

    2. List and Recursive

    有了上面的樣式比對,就可以寫出非常漂亮的遞迴函式:

    defmodule Factorial do
      def calc(0), do: 1
      def calc(val), do: val * calc(val - 1)
    end
    

    以及 list 處理函式:

    defmodule Sum
      def calc([]), do: 0
      def calc([ head | tail]), do: head + calc(tail)
    end
    

    3. Pipe operator

    我們常常看到這種程式:

    request = generate_request
    response = get_response(request)
    body = parse_body(response, :html)
    html = render(body)
    

    因為若想省去中間那些暫時的變數,就要寫成這樣:

    html = render(parse_body(get_response(generate_request), :html))
    

    這看起來更醜,你得要從裡面讀到外面,而且還常常找不到開始讀的點。

    Pipe operator |> 可以讓你把回傳值當成下一個函式呼叫的第一個參數。所以上面的例子會寫成:

    html = generate_request
           |> get_response
           |> parse_body(:html)
           |> render
    

    當看到上面的寫法之後,我好像看懂了底下這句話:

    Functional programming is like a jounery of data transforming.


    稿擠,有機會再來寫:

    • The function capture notation
    • Spawn a process
    • pmap
    • Meta-programming with marco
    • Mix and Doctest


    What I feel until now:

    之前有短暫的摸過 Clojure,也唸了兩章 SICP。不過這幾個週學 Elixir,時不時有種頓悟的感覺。好像又多了解 Functional programming 一點點。更重要的是,這語言到目前為止,讓我獲得非常多的樂趣。

    Happy hacking!

  • Listen Jazz: Improvisation

    話說幾年前有個部落格叫「爵士道 (Jazztao)」,雖然生性散漫的只寫了三、五篇文章。最後還因為閃神不小心砍掉整個 Google 帳號而從此沉沒。但有時想起還是會好奇著:「要是那時繼續寫下去,會是怎麼樣子呢?」

    在那個沉沒的部落格裡,我最懷念的一篇是叫:「二十分鐘,弄懂爵士樂在搞什麼鬼 - 談爵士樂的進行模式與即興。」想說就試著從這裡再接關看看吧。

    那篇文章是這樣開始的: 先想像一個賽車跑道。電視上看過的也好,遊戲裡玩過的也好。就是那種有直線,也有很多轉彎的長長柏油路賽道。照慣例也總會有個黑白方格交錯看板的起點/終點。


    而對爵士樂手來說,這個跑道的一圈,長得像是這個樣子:

    這張樂譜寫上了主旋律,以及每個小節的和弦。範例中這首 Autumn Leaves 應該是許多人耳熟能詳的曲子。我在 Youtube 上找了個很有名的版本放在底下。而在你開始播放之前,要提醒你兩件事情。第一是底下的文章會有很多 分:秒 的時間標記。看見時記得回頭瞄一下播放器,留神聽聽看那些時間點前後的聲音有什麼不同。


    剛開始是前奏,鋼琴、Double Bass 跟鼓。 Saxphone 一句一句,像是賽前的暖身。小號加了一些刺刺的聲音。00:54,開始從起跑線,也就是樂譜的第一小節出發了,小號奏起你熟悉的旋律。到 02:01 時,樂譜已經跑到了盡頭。但這只是第一圈而己,對爵士樂來說,故事才正要開始精采。

    跟賽車一樣,鼓、Double Bass 跟鋼琴回到樂譜的起點,將相同的和弦進行再重覆一次 ( 上方的 Am7D9是所謂的和弦。和弦的組合順序稱之為 “和弦進行” ) 。而樂手中,會有一個人跳出來開始「即興」。

    所謂的「即興」,就是在這些和弦進行裡,當下想出另一條可行的旋律線。樂理上來想例如 「Am7 - D9 - GMaj7 可以處理成 II-V-I」,雖然原理上是這樣,樂手計算的速度得要比說這句話的時間短上許多。大家也會試著探索更刺激,更有趣的聲音可能性。故意繞到和弦邊邊,弄出很不協調的聲音再接回來。大概就是「賽車時壓上山路的水溝蓋超車」那麼一回事吧。「怎麼會想出這樣的聲音呢?」在爵士樂裡,是再好不過的稱讚了。

    一路開到03:11,Saxphone 手 Cannonball Adderley 還沒玩過癮,繼續再跑一圈。旋律又跟上一圈完全不同。一開始是高低音大跳躍的句子,中間用了許多切分音玩不一樣的節奏。最後再來很多快速的長短句。

    曲子走到 04:21,小號手 Miles Davis 接手。不像 Cannoball 那種快速的句型,他的語法比較不慍不火,卻總是能在棒的要命的時機丟出難度很高的句子。05:30又是第二圈,長音加上許多的短句。起頭聽起來很像原來的旋律,繞個彎卻又是另一番風景。

    鋼琴手 Sam Jones 在 06:40拿下接力棒,開始這首曲子的第六圈。在很像原曲句子的變奏裡,把腳偷偷踩出線,又漂亮的踩回來。

    07:46,結束了各自的炫耀時間,大家回到主旋律再跑上最後一圈。08:30鋼琴手在半圈的地方意猶未盡般獨奏了一小段。08:57曲子跑完,又用前奏的和弦多跑了幾個小節的尾奏才結束這首曲子。

    那麼故事就是這樣了。除了早期 Big band 時代之外,四零年代中期的 Bebop 樂手們開始確立了這種「樂曲只是載具、即興才是本體」的競技,這個格式便成為爵士樂的慣例。要分辨「爵士樂」或是「具有爵士風格的流行樂」,看看是不是在兩個「主旋律」間,夾了「整數倍長度的即興」,就是一個很好上手的判斷方式。

    試著去 Youtube 上找找看其它的 Autumn Leaves 版本。或是看看影片那張封面圖,找找那些樂手的名字。在那些名字或是曲名背後,有著無數有趣的聲音等著你去發現。

    剛剛在放音樂之前,說過要提醒兩件事。那麼第二件事情,就是這張叫 “SOMETHIN' ELSE” 專輯的每一首都很好聽。記得去買。


    後記: 這篇文章在我的草稿夾裡躺了一年。外頭下著大雨,想到就信手改一改發了。如果有想到什麼好的切入點,就再看看有什麼可以寫的吧。

    廣告: 我的朋友華勁,六月底會在台中開課講 紐約爵士現在在搞什麼

    廣告 Part II: 明諺新專輯的巡迴表演,從明天晚上台北 Sappho 開始。

  • Meta and meta-programming

    「我的語言的界限,就是我的世界的界限。」 – Ludwig Wittgenstein

    最近在讀 meta-programming 的東西,翻到一些網路上的爭論。想到幾年前,一個通曉多國語言的日本朋友問我:「meta-programming 裡的 meta 這個字在台灣怎麼翻?」,那時我是麼回的:「有時候翻中介,有時候翻後設,有時就乾脆不翻了。」

    我認為 meta 的翻譯,就是爭論的來源。

    無論是把 “meta-programming” 翻成「中介編程」,還是把 “meta-fiction” 翻成「後設小說」,在字面上都只是讓我更困惑而己。meta 這個字其實很有趣,基本上就是「關於 X 的 X」。舉幾個例子:

    • 卡爾維諾的「如果在冬夜,一個旅人」,是本講述一些人在讀小說的過程的小說,這叫 meta-fiction。
    • 「我們手邊的使用者資料正確性有多高」這個數字,本身也是一個資料。所謂 meta-data,就是是關於資料的資料(data about data)。

    所以你現在一眼看到「六個尋找作者的劇中人」這個劇本,還有「Design of design」,就知道它們一付 meta 樣。


    回頭來看 meta-programming ,幫我們產生/修改程式的程式只是 meta-programming 的其中一部份而己。若用「關於程式的程式」來理解,「將程式碼本身當做資料在操作的程式」才是 meta-programming 的全貌。來個最近學到的超無用程式範例:

    class Suprise < (rand < 0.5 ? Hash : Array)
    end
    
    sup = Surprise.new
    sup[0] = 'wow!'
    p sup #可能是陣列 ["wow!"],也可能是Hash { 0 => "wow!" }
    

    第一行的後半段,我們將 Hash 或是 Array 這兩個類別當做資料在操作,Surprise各有 50% 的機會繼承 Hash 或是 Array。我認為這是不產生/修改程式碼的 meta-programming 好範例。另外提一下,如果你在 production code 裡用了這個技巧,千萬不要說是我教你的。

    看一下 JavaScript 這個對 meta-programming 非常友善的語言,由於物件跟 Hash 是同一種東西,因此我們把使用 ["string"] 的形式動態的從物件中調值視為理所當然。在其它的語言中,這件事並非總是這麼簡單。

    Ruby 還可以輕鬆的用 send() 解決:

    obj.send(:method_name)
    

    在 Java 裡就得大費周章的使用 reflaction:

    MyClass.class.getMethod("methodName").invoke(someArgs)
    

    再來看看 JavaScript 頗受壞評的 eval:

    eval("function magic(){ alert('rabbit!'); }");
    magic();
    

    所以當資料與程式碼的界限開始模糊的時候,你知道你已經跨進 meta-programming 的界限裡了。你也該知道,這一段程式碼要是出錯了,在 debug 時可能沒那麼容易。


    那時我回問了我朋友:「那你覺得有什麼比較好的翻法嗎?」朋友說:「我覺得翻成"元編程"跟"元資料"蠻不錯的。」 想了想,我也這麼覺得。

  • 客製 Mac 的 CLI

    UPDATE:

    後來我改用 prezto 來取代 ohmyzsh,功能跟佈景主題比較少,但啟動速度較快.

    Further Update:

    再後來就用純 zshrc + zshenv 改了。可以參考 我的 dotfiles


    之前答應的事欠很久,就用這篇來還吧。

    身為 Ruby/Rails 開發者,每天跟 Command Line Interface (CLI) 混在一起是再正常也不過的了。

    之所以買 Mac,就是因為 Mac 的 CLI 跟 Windows 渣一般的命令提示字元不是同一個等級的東西。即使如此,要是每次打開內建的終端機,都看到是下圖這個樣子,也很難不心生畏懼一下的。

    Original OSX CLI

    既然是每天拿來吃飯的工具,我自己用了 iTerm2,Oh My ZSH,再選一個喜歡的 monospace 字體,把它弄成這樣:

    Prettier OSX CLI

    monospace 字體

    好用的 monospace 字體其實蠻多的,可以從這裡挑一個。基本上就是 1lI0oO 要分的清楚,其它就是個人喜好了。我目前用的是 Source Code Pro,之前則是 Bitstream vara sans mono

    iTerm2

    iTerm2 不是必要的,但它提供了原生的 Terminal.app 所沒有的幾個好用功能:

    1. command + click to open file
    2. Split pane view
    3. Hotkey Instant terminal anywhere
    4. Highlight word when searching
    5. Mouseless copy
    6. Paste history

    官網下載後解壓縮,然後丟到 Application 裡。

    執行 iTerm2,到 Preference 改 Profiles => Default => Text 選擇你剛裝好的字體。

    進階自訂選項

    SMYCKrailscasts theme 都是蠻不錯的配色。下載後點兩下匯入,再去 Preference 改 Profiles => Default => Colors。

    oh-my-zsh

    oh-my-zsh 讓你可以不用再辛苦的手刻 config file,就內建一堆好用的 plugin(*註), 還提供一堆主題讓你挑選。因為我比較喜歡 zsh 的補完功能,如果你還是想用 Mac 預設的 bash,bash-it 看來可以做到類似的事。但因為我沒用過,所以請自行踩雷。

    安裝 oh-my-zsh 的方式,就是打開剛剛裝好的 iTerm2 ( Terminal.app 也可以),貼上這行指令:

    curl -L http://install.ohmyz.sh | sh
    

    更多 Oh My ZSH 的設定可以參考 RailsCasts 的影片

    進階自訂選項

    zshthem 挑一個順眼的主題,記下上方的名字。(裡面沒有列出全部可用的主題,像是很多人愛用,但是需要特殊字體的 agnoster)。我用的是 af-magic。

    修改 ~/.zshrc ,把 ZSH-THEME="xxxxx" 的xxxxx改成你剛剛記下來的字,儲存。然後回到 iTerm2,輸入 source ~/.zshrc


    提醒

    • ~/.zshrc 底下的 plugins="(.....)" 千萬要慎選。看清楚 ~/.oh-my-zsh/plugins/ 裡的相應的檔案內容。我曾經被 bundler plugin 搞掉好幾個小時。

    • 指令上色是用 zsh-syntax-highlighting 做的。 想要的話就參考連結裡的說明。

  • 在 OS X 安裝 git 環境

    基本上就是某篇文章的片段翻譯。

    I. 安裝 Command Line Tools

    首先需要安裝 OSX Command Line Tools。 如果你沒有開發 iOS 或是 Mac App 的需求,可以直接到Apple developer site (免費,需註冊) 下載 Command Line Toos for Mountain Lion 並安裝,可以省下約 4GB 的空間。

    若想安裝 Xcode,就照以下的步驟操作

    1. 開啟 App Store,搜尋 Xcode 再點安裝

    2. 執行 Xcode,在選單列依序點選Xcode -> preferences

    3. 切換到Downloads -> Components分頁中。

    4. 按下Command Line Tools項目旁的install按鈕。

    II. 安裝 Homebrew 套件管理工具

    Homebrew 是 OS X 上相當好用的套件管理員,可以想成是命令列版的 App Store 之類的。 安裝的方式是執行終端機。(在 Spotlight 裡輸入terminal就會找到了) 接著複製底下的命令,貼到終端機上,按Enter執行。

    ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
    

    III. 用 Homebrew 安裝 git

    在終端機上輸入

    brew install git
    

    IV. 設定 git 使用者

    在終端機上分別輸入以下兩行,記得把名字跟 email 改掉。

    git config --global user.name "你的名字"
    git config --global user.email "你的Email"
    

    V. 錯誤訊息大逃殺

    請參考本篇

  • 在 Linux 安裝 Rails 環境

    安裝必要元件

    開啟「終端機模擬程式」(在 Menu 裡),分別輸入底下每一行指令後按Enter。先不要整塊複制貼上,得一行行輸入。如果指令很長懶得打的話,在 Linux裡開瀏覽器 (Menu -> 網際網路),找這篇文章一步步複製貼上。

    sudo apt-get update
    

    按下Enter後會要求你輸入密碼,按密碼時螢幕沒有反應是正常的,就打進去按Enter

    sudo apt-get upgrade
    

    完成後中間若有要求取代檔案的訊息,一直按Enter同意。

    sudo apt-get install curl build-essential zlib1g-dev libssl-dev libreadline-dev xclip git
    echo "alias clipboard='clip -sel clip'" >> ~/.bashrc
    

    上面的 zlib1g 請拼對,前面是L後面是1,在這裡卡關就太冤了。


    安裝 rbenv

    首先安裝 rbenv

    git clone git://github.com/sstephenson/rbenv.git .rbenv
    echo "export PATH='$HOME/.rbenv/bin:$PATH'" >> ~/.bashrc
    echo 'eval "$(rbenv init -)"' >> ~/.bashrc
    exec $SHELL
    

    再安裝 ruby-build

    mkdir -p ~/.rbenv/plugins
    cd ~/.rbenv/plugins
    git clone git://github.com/sstephenson/ruby-build.git
    echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
    exec $SHELL
    

    總算要開始裝 Ruby 跟 Rails 了。

    rbenv install 2.1.2
    rbenv rehash
    rbenv global 2.1.2
    ruby -v
    gem install bundler rails
    rbenv rehash
    rails -v
    
  • 在 VirtualBox 安裝 Linux

    1. 下載 Linux Mint。或是你喜歡 Ubuntu,也可以下載桌面版本,但是我在 VirtualBox 上跑 Ubuntu 12.04 的安裝程式慢到令人髮指,所以就裝 Linux Mint 13 Xfce, 64 bit 了…
    1. 下載 VirtualBox,這兩個要下載好一陣子,吃個午餐再回來吧…

    2. 安裝 VirtualBox 後執行,按下[新增],名稱隨便你打,類型是 [Linux],版本都是 [Ubuntu (64 bit)]。[繼續]~

    3. 記憶體設 1024MB* -> 立即建立虛擬硬碟 -> VDI -> 動態配置 -> 磁碟 30GB* -> 建立。

      • 打*的是跟預設值不同的。
    4. 在左邊清單找到剛剛建好的機器,在上面按滑鼠右鍵,選擇右鍵選單裡的【設定值】,按上面的[顯示],把「視訊記憶體」調到最高。

    5. 再按上面的[存放裝置],在「IDE 控制器」底下的「空」上面按一下滑鼠左鍵,在右邊「屬性」區塊裡找到光碟圖示,再按下滑鼠左鍵,按【選擇虛擬 CD / DVD 光碟檔案】。接著在檔案總管裡找到剛下載的 Ubuntu 或是 Linux Mint 的 iso 檔。把光碟檔掛載好之後,按下[確定]關閉對話盒。

    6. 回到 VirtualBox,按下上方的[啟動],等機器開機之後,用滑鼠雙擊桌面上的「Install Linux Mint」(Ubuntu 不需要點光碟,直接就會進安裝畫面),接著在左邊的語言清單捲到最下面,選[中文(繁體)],原則上接下來就像你每次重灌那樣不斷的按[繼續],就會裝好了。()什麼?你不會重灌?你確定你用的是 Windows 嗎?)過程中請注意設定帳戶名稱及密碼時,一定要選個好記的密碼。就算勾選了自動登入,以後還是會很常用到這組密碼,弄丟了可是很麻煩的。

    7. 重新開機之後,記得電腦要接上網路,點選桌面右下角工作區像是盾牌的圖示,輸入密碼後會出現「更新管理員」,點選上面的「安裝更新」,你就可以先去睡一覺,醒來之後就裝完了。(這步會非常久)

  • Robert Nyman: 解釋 JavaScript 的 scope 及 closures

    當發現我沒辦法向別人清楚的解釋一件事的時候,就會有一種「其實我自己也沒有弄的非常清楚」的認知。前陣子想解釋 JavaScript 裡的 closures 以慘敗收場。網路上翻到一篇文章看了之後有點「啊原來是這樣」。細讀之餘就順手翻譯一下,也許有人能用得上。

    Closure 中文常翻成’閉包',不過這種看了也不會更懂的專有名詞,就留著不翻了…而 Scope 是變數的’作用域’或是’有效範圍',依上下文需要翻或不翻。Rebort Nyman 在 2008 寫了原文,請往 Original Post 找。


    背景

    有許多的文章試著解釋 scope 及 closures ,但基本上我得說他們大多數都沒辦法解釋的非常清楚。此外其中一部份的作者預設每個人都開發過15種以上的其它語言,但依我的經驗來說,寫 JavaScript 的人通常具備的是 HTML 及 CSS 的背景,而非 C 或是 Java。(譯注: 在 Node.js 及 Ajax 興起的今天,也許這情況有點改變。)

    因此本文謙遜的目標,是想讓大家都能領會到 scope 跟 closure 是什麼,它們如何運作的,以及該如何妥善的運用它們。在閱讀本文之前,你得先有一些變數及函式的基礎知識。

    Scope

    Scope 代表變數及函式能夠被存取的範圍,以及它們在什麼樣的文本中被執行。一般來說,變數及函式可以定義在全域範圍或是區域範圍中。變數有所謂的’函式作用域',而函式也有像是變數一樣的作用域。

    Global Scope (全域範圍)

    當某個東西是全域的,代表它在你程式碼的任何地方都可以被存取,看一下以下的範例:

    如果在瀏覽器執行底下的程式碼,monkeygreetVisitor 這兩個變數的存取範圍會是 window。因此所有跑在同一個頁面下的程式都能存取這兩個變數。(譯注: 我把程式碼放到 JsFiddle 中,並依需要調整及註解,你可以按下 Result 來看結果。)

    Local Scope (區域範圍)

    與全域範圍相反,local scope 是只宣告在程式碼的某一個區域中,也只能在這個區域中存取的東西。例如函式內部就是這些區域的一種。舉例來說:

    如果你看一下上面的代碼,saying 這個變數只能在 talkDirty 函式內部存取。在函式的外面,它根本就沒有被定義。特別要注意的是,如果你在第二行沒有用關鍵字 var 來定義 saying,那它會自動變成全域變數。

    這也代表如果你有巢狀的函式時,內層的函式能夠讀到在外部函式裡所定義的變數及函式:

    如你所見,內層函式 capitalizeName 不需要傳入任何參數,但是它就能夠存取定義在外部函式 saveName 中的 firstName 這個參數。再用一個例子讓我們把事情弄的更清楚一點:

    如你所見,兩個內層函式都可以存取外部函式的 siblings 陣列,而兩個同級的內層函式也可以彼此存取(如本例中 joinSiblingNames 去存取 siblingCount)。然而定義在 siblingCount 函式中的 siblingsLength 變數,只能在該函式內部使用,這就是它的變數範圍。


    Closures

    現在你應該比較了解什麼是變數範圍了,我們把 closures 加進來看。Closures 是一種將變數設為特定內容來運作的表達式,通常是函式。說的簡單一點,當內層函式存取了外部函式的變數,就會產生 closure(* 請看 note )。舉例來說:

    哇,!剛剛發生什麼事了? 我們一步步拆來看:

    1. 當呼叫 add 函式時,它會回傳一個函式。
    2. 那個回傳的函式封閉它的內文,並記憶封閉時x參數的值。(也就是上述程式碼中的 5)
    3. 我們把回傳的函式指定到 add5 變數上,它會一直記得它建構當時 x 的值。
    4. 而這個 add5 變數代表一個會永遠把傳入參數加 5 的函式。
    5. 這就表示當我們呼叫 add5 時,傳入參數 3,它就會把 5 跟 3 相加,然後回傳 8。

    因此在 JavaScript 的世界中,這個 add5 函式實際上看起來像這樣:

    function add5(y){
      return 5 + y;
    }
    

    Note

    一段討論之後,我想更正成:

    Closure 會在底下兩個條件為真時產生:

    1. 內部的函式存取了外部函式的變數。
    2. 內部函式能被外界直接調用。

    惡名昭彰的迴圈難題

    你有沒有遇過試著用某些迴圈,把 i 指定成變數值,卻發現全部都回傳最後一個值的情況?

    不正確的參照

    我們來看看這個壞掉的範例,它會建立五個元素,把 i 設成顯示文字。再為每個元素綁定一個 onclick 事件,按下去後會 alert 這個元素的 i 值。也就是說會 alert 顯示文字的值。然後再把元素加到 document body 裡:

    每個元素都顯示了正常的文字,也就是 “Link 0”,“Link 1” 等等。但不管我們按下哪一個,都會 alert 數字 “5”。真要命,為什麼會這樣? 原因是這個 i 的值在每一次迴圈處理都會加 1,而既然 onclick 事件還沒被觸發,只是綁定到元素的事件上,i 的值會一直累加上去。(譯註:可以按 Result)

    因此這段迴圈一直循環到 i 變成 5,addLinks 函式結束。然後呢,不管哪一個 onclick 事件被觸發,它都會拿到 i 變數最後的那個值。

    正確的參照

    而你應該做的,是建立一個 closure,這樣當你在把 i 綁定到事件上時,它會取得當下的那個值。像這樣:

    用這段程式時,如果你按下第一個元素,它會 alert0,第二個元素會 alert1,依此類推,像是你看到上一段程式碼時期望的運作方式。這裡的解法是綁定 onclick 事件的那個內層函式創造了一個參照 num 參數,也就是 i 當時的值的 closure。

    而那個函式會把該數值閉鎖,安全的藏起來。等到觸發 onclick 事件時,能夠回傳對應的正確數值。


    Self-invoking functions

    Self-invoking functions 是一種立刻執行,並建構自己的 closure 的函式。看一下這個範例:

    好嘛,所以那個dog變數只能在內文裡存取。有什麼了不起啊,就是個藏起來的狗…但是朋友,這就是它真正有趣的地方。這解決了我們上面迴圈的狀況,而且這也是Yahoo JavaScript Module Pattern 的基礎。


    Yahoo JavaScript Module Pattern

    這個 pattern 基本上是用一個 self-invoking function 來建立一個 closure,從而讓 JavaScript 物件能夠有公開及私有的函數跟屬性。像這個簡單的例子:

    這樣作的美好之處,在於從此你可以自己決定物件上的哪些東西要公開(以及可以被複寫),而那些是私有的,不能存取而且不能被更改。上面的 name 變數在函式外部看不到,但是能夠用 getName 函式取值及用 setName 函式設值。因為這兩個函式是建立了 closure 並參照了 name 變數。


    結論

    我誠摯的希望在看完這篇之後,無論是新手或有經驗的程序員,都能夠清楚的領會 scope 及 closures 在 JavaScript 實際上是怎麼運作的。歡迎各種問題及回應,而如果你的建議夠重要,我會把它加到我的文章裡。

    Happy coding!

    (原文完)


    延伸閱讀:

  • An Apple Hotkey a day

    Update daily? Well, it’s just a slogan.

    Bash Hotkeys

    • control + A : move cursor to line start.

    • control + E : move cursor to line end.

    • control + U : clears the line content.

    • control + L : clears the screen content.

    • control + W : cut the word before cursor.

    • option + D : cut the word after cursor.

    • option + F : move the cursor forward one word.

    • option + B : move the cursor backward one word.

    Note : In iTerm2, you have to set option work as ‘+Esc’ in ‘Preference → profile’ for moveing cursor, otherwise it will send the special characters.


    Page Up / 上一頁

    Fn +

    Page Down / 下一頁

    Fn +

    Home

    Fn +

    End

    Fn +


    Move cursor to end of line / 移動游標至行尾

    command + or control + A(bash hotkey)

    In most of the cases, the first one should do the job. But in some application which the first key-binding maps to something else, e.g. iTerm2, you have to use the bash hotkeys.

    Move cursor to begin of line / 移動游標至行頭

    command + or control + E(bash hotkey)

    Select content from cursor to begin of line / 選取游標至行頭的內容

    command + shift +

    Select content from cursor to end of line / 選取游標至行尾的內容

    command + shift +


    Open file/application / 開啟檔案/程式

    command + O or

    command +


    Hide/Show the dock from screen. / 顯示/隱藏下方 Dock

    command + option + D or

    control + F3


    Cycle through windows in current application. / 在同一應用程式的不同視窗中循環切換

    For example, if you open servel windows of Chrome Browser, this hotkey will focus on each window iterately.

    舉例來說,假設你在 Chrome 瀏覽器中開了數個視窗,此熱鍵會在輪流將各個視窗設為目標視窗。

    command + `

    ps: ` Backtick, or grave accent, which is above Tab key.

    `抑音符位置在Tab鍵的上方.


    Turn cureent application to “Full screen mode” / 將目前的應用程式切換為"全螢幕模式" (Above Lion)

    command + control + F


    Search this word in Dictionary.app / 在字典中查詢目前單字 (Above Lion)

    Select the word first, then press: command + control + D

    ps: Might not work in some applications.

    先圈選想查詢的單字,接著按下: command + control + D

    ps: 在某些應用程式下無法使用


    Input Accented Characters / 輸入帶有重音符號的字母 (Above Lion)

    Hold the key for 1 sec.

    ps: Might not work in some applications.

    按住該字母一秒以上。

    ps: 在某些應用程式下無法使用

subscribe via RSS