導入 / Introduction
Haskellが好きな後輩に「Common Lispも良いよ! HaskellはLispの影響も受けてるらしいよ!」と勧めたところ「Lispは括弧が多いのがちょっと…」と言われたので、Haskellの$演算子風のCommon Lispマクロを定義し、Common Lispのコードから括弧の数を減らすことを試みました。
When I recommended Common Lisp to a junior who loves Haskell, saying “Common Lisp is great too! Haskell is said to be influenced by Lisp!”,
they replied, “But Lisp has too many parentheses...”.
So I tried to define a Common Lisp macro inspired by Haskell’s $ operator to reduce the number of parentheses in Lisp code.
Haskellの$演算子 / The $ Operator in Haskell
書籍「すごいHaskell たのしく学ぼう!」に「\$は、はるか右に閉じ括弧のある開き括弧だと考えることもできるでしょう。」という記載があります。
本記事ではCommon Lispで「$を、はるか右に閉じ括弧のある開き括弧とみなす」というマクロを定義し、そのマクロを使用して関数を定義します。
In the book Learn You a Haskell for Great Good!, it says:
“You can think of \$ as an opening parenthesis that has its matching closing parenthesis far to the right.”
In this article, we define a Common Lisp macro that treats $ in the same way, and use it to define functions.
Common Lispマクロを使用した関数定義 / Defining Functions Using the Macro
引数のリストの合計を求める関数「sum」と引数の階乗を求める関数「factorial」の定義を、マクロ「with-haskell-dollar」を用いて行います。
コメント「lisp function」で囲った部分と比べて括弧の数が少ない!すごいでしょ?(もちろん動きます。)
Here we define two functions — sum (which sums a list) and factorial — using our with-haskell-dollar macro.
Compared to the “lisp function” version, there are fewer parentheses! Cool, right? (And yes, it actually works.)
#|
Example of using macro "with-haskell-dollar"
|#
(with-haskell-dollar
defun sum (l)
$ if (null l)
0
$ + (car l) $ sum $ cdr l)
#| lisp function
(defun sum (l)
(if (null l)
0
(+ (car l) (sum (cdr l)))))
|#
(with-haskell-dollar
defun factorial (n)
$ if (<= n 1)
1
$ * n $ factorial $ 1- n)
#| lisp function
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (1- n)))))
|#
Common Lispマクロ「with-haskell-dollar」の定義 / Definition of the with-haskell-dollar Macro
#|
Definition of macro "with-haskell-dollar"
|#
(defmacro with-haskell-dollar (&rest body)
`(,@(dol2bkt body)))
(defun dol2bkt (lst)
(if (null lst)
nil
(if (eq (car lst) '$)
(list (dol2bkt (cdr lst)))
(append (list (car lst)) (dol2bkt (cdr lst))))))
with-haskell-dollarで囲まれたコード(list)全体を関数「dol2bkt」(dollarから括弧へ)に渡します。
dol2bktは、listの先頭が\$のときは、listの2番目以降をdol2bktに渡します。
dol2bktは、listの先頭が\$以外のときは、listの先頭と「listの2番目以降をdol2bktに渡した結果」のlistを返します。
The entire code (as a list) inside with-haskell-dollar is passed to the helper function dol2bkt (“dollar to bracket”).
If the first element is $, it recursively processes the rest of the list.
Otherwise, it returns a new list combining the first element and the recursively processed remainder.
最後に / Conclusion
これで括弧恐怖症のHaskellerも安心ですね!
Now, even Haskellers with parenthesis-phobia can code in peace! 😄