elm: 解 common factors

作者:   發佈於:   #elm

偶爾會看到有人提到 elm 這個程式語言,於是就稍微花了一點時間來學了一下。基本上是個在語法上有點類似 Haskell 的函數性語言,靜態型別、資料皆不可變動、"No Runtime Exceptions" 、編譯標的是 Javascript,看來可能會加入 wasm。都是一些很有個性的賣點。

為了練習,就把 Perl Weekly Challenge 082 中 common factors 這題拿來再寫一回。

elm 網站教學來練習的話,最後都是做出 Web UI。所以可以在瀏覽器裡展示:play/elm/common-factors。源碼:play/elm/common-factors/src/Main.elm

實際上編譯過程中出現的錯誤訊息真的頂有幫助的,無論是錯誤位置與錯誤理由都很精準。如果把函數名稱有錯字,也會像 raku 一樣會列出一些與錯字類似的關鍵字給程式設計師看。這年頭的新興語言中都得把錯字修正及推薦系統做為編譯器內建標準配備才行了喔?

依「列出因數」這題目定義,若 Web UI 的兩個輸入欄位內容如果不是正整數,基本上就無法計算。與到這種狀態時,就讓輸出變成問號。輸入欄成為可計算狀態的話,輸出就是幾個正整數。

比較核心部份的幾個函數如下。演算法同前文,一樣是先找出兩數之最大公因數,再取得該數字之所有因數。

factors : Int -> List Int
factors n =
    List.range 1 n |> List.filter (\x -> (modBy x n == 0))

gcd : Int -> Int -> Int
gcd m n =
    if n == 0 then
        m
    else
        gcd n (modBy n m)

commonFactors : Int -> Int -> List Int
commonFactors m n =
    if m > 0 && n > 0 then
        factors (gcd (max m n) (min m n))
    else
        [0]

這裡 commonFactors 傳回值的型別是 List Int,也就是整數串列,但空串列 [] 並不符合這型別。所以弄了個 [0] 這種特殊值。在 UI 端如果碰到 0 就顯示為問號,基本上表示輸入是無法處理的狀態。或許可以改用 Maybe 型別來處理這種狀況。

原本以為函數能依其參數型別而有多重分派,但試了之才發現沒有,而是在函數內用 if-else 來處理。或許因此,這語言特別規定 if 後面一定要接 else,並且兩邊的算式所導得之值之型別必需相同。

HTML UI 的制作是依賴一大堆與 HTML 標籤同名的函數,類似 hackage Text.Blaze (BlazeMarkup)。如下:

view : Model -> Html Msg
view model =
  div []
    [
     text "M ="
    , input [ placeholder "M", value model.inputM, onInput ChangeM ] []
    , br [] []
    , text "N ="
    , input [ placeholder "N", value model.inputN, onInput ChangeN ] []
    , br [] []
    , div [] [
          text ("common factor = " ++ computeCommonFactors (validated model)) ]
    ]

跟以前用過的 Markaby、Markapl、Markaya、Template::Declare 同出一徹。

elm 這語言本身的語法規則似乎不多、不難記。雖然其介紹詞與 CoffeeScriptLiveScript 同樣都是 "A language which compiles to Javascript" (這樣算是有介紹到嗎?),不過,由 elm 編成的 Javascript 與其他兩者差異很大,並不算是能讀得懂的狀態。文件上看來,與 Javascript 互通的方式十分伺限,這應該有設計上的理由。