Shenについて
Shenは、下記の特徴を持ったLisp系の言語です。
特にパターンマッチングや静的片付けに関する部分が気になりました。
- パターンマッチング
- 一貫性のあるラムダ計算
- マクロ
- 遅延評価(がオプションである?部分的に?ちょっとわかりませんでした)
- 静的型付け
- functional Prologとの統合(?)
- コンパイラコンパイラ
インストール
Downloadからソースをダウンロードできます。
実行環境はいくつもありますので、お好みのものをお選びください。
今回は手元にあったRuby版を採用。下記環境で試しています。
- Windows 7
- Ruby 1.9.3
gem install shen-ruby
で簡単にインストールできます。
実行
実行はsrrepl
でOK。
以降は、Shen in 15 minutesの内容を、順不同にかいつまんで書いていきます。
>srrepl
Loading.... Completed in 7.48 seconds.
Shen 2010, copyright (C) 2010 Mark Tarver
released under the Shen license
www.shenlanguage.org, version 14
running under Ruby, implementation: ruby 1.9.3
port 0.8.0 ported by Greg Spurrier
(0-)
最後の(0-)というのは、0番目の式の入力プロンプト、-
記号は型チェックしていない状態を意味します。
型チェックを有効にするには(tc +)
と実行すればOK。
※以降、入力プロンプト内の番号は省略します
(-) "abc"
"abc"
(-) (tc +)
true
(+) "abc"
"abc" : string
型を語って来ました。折角なので、type checkingを有効にしたまま次に進みます。
リストは[ ]
で表します。リストの中には、type checking有効時には、型の互換性があるものしか入れられない模様です。
(+) a
a : symbol
(+) 1
1 : number
(+) [a b c]
[a b c] : (list symbol)
(+) [a 1]
type error
式の実行はいつものLispです。
(+) (+ 1 2 3 4 5)
15 : number
(+) (and true false)
false : boolean
試しにand
の第2引数にnumberを入れてみたところ、当然怒られましたので、andの型を見てみます。
なんと、and
は、2つのbooleanを受け取り1つのbooleanを返す関数だったのです(そりゃそうだ)。
それよりも、Haskellのような記法で型の情報が出力されていることが気になります。
これはまさか…と、(and true)
と実行した結果、1つのbooleanを受け取り1つのbooleanを返す関数が返ってきました。
何とカリー化も用意されている模様。
(+) (and true 3)
type error
(+) and
and : (boolean --> (boolean --> boolean))
(+) (and true)
#<Proc:0x3b2a6e8 (lambda)> : (boolean --> boolean)
自分でも式を定義してみます。試しに、2乗を返す関数を定義してみます。
関数名、型、処理内容の順番で書くようです。また、type checkingを無効にしている場合、型の記載は不要とのこと。
(+) (define square
{number --> number}
X -> (* X X))
square : (number --> number)
(+) (square 4)
16 : number
何となく雰囲気がわかってきたので、そろそろFizzBuzzを書いてみようかと。
FizzBuzzを書いてみる
FizzBuzzを関数型ちっくに解くには、以下が必要です。
- number --> string のfizzbuzz関数
- 1, 2, 3, ... となる数値リストの生成
- mapで数値リストをFizzBuzzの文字列リストに変換
number --> string のfizzbuzz関数
※業務連絡 ここからまた(tc -)
します。何故か余りの計算方法が(tc +)
のときにわかりませんでした…
fizzbuzz関数は下記のように定義します。パターンマッチングを用い、15で割り切れる場合、3で割り切れる場合、5で割り切れる場合、それ以外の4パターン用意します。余りの計算はshen.mod
関数を使う模様。
手動テストの結果、うまく動いている模様です。
(16-) (define fizzbuzz
X -> "FizzBuzz" where (= 0 (shen.mod X 15))
X -> "Fizz" where (= 0 (shen.mod X 3))
X -> "Buzz" where (= 0 (shen.mod X 5))
X -> (str X))
fizzbuzz
(17-) (fizzbuzz 1)
"1"
(18-) (fizzbuzz 2)
"2"
(19-) (fizzbuzz 3)
"Fizz"
(20-) (fizzbuzz 4)
"4"
(21-) (fizzbuzz 5)
"Buzz"
(22-) (fizzbuzz 6)
"Fizz"
(23-) (fizzbuzz 15)
"FizzBuzz"
(24-) (fizzbuzz 16)
"16"
1, 2, 3, ... となる数値リストの生成
探してみたのですが、標準での取得方法がわかりませんでした。ですので、とりあえず100まで生成する関数を自作します。
(56-) (define number-list
X Y -> [] where (> X Y)
X Y -> [X | (number-list (+ 1 X) Y)]
)
number-list
(57-) (number-list 1 10)
[1 2 3 4 5 6 7 8 9 10]
mapで変換
mapは標準関数としてありました。これで、数値のリストをfizzbuzz関数で変換することができます。
(60-) (map fizzbuzz (number-list 1 20))
["1" "2" "Fizz" "4" "Buzz" "Fizz" "7" "8" "Fizz" "Buzz" "11" "Fizz" "13" "14" "FizzBuzz" "16" "17" "Fizz" "19" "Buzz"]
何とかFizzBuzzができた…
ハマった点
type checking有効時に余りの計算が出来なかった件。実行環境のバグだとは思いますが、ハマりました…
(67+) (tc -)
false : boolean
(68-) shen.mod
shen.mod
(70-) (shen.mod 5 3)
2
(71-) (tc +)
true
(72+) shen.mod
shen.mod : symbol
(73+) (shen.mod 5 3)
type error
感想
Shenのパターンマッチングを使った関数の書き方や静的型付けは、私が知っているSchemeやClojureとは違った書き方ができるので面白いと思います。
しかし一方で、今時点でShenにガッツリハマろうとは思いませんでした。理由は実行環境が複数あり、どれが本命の実行環境か分からないことです。想像ですが、複数の実行環境があると、それだけ開発者が分散してしまうのではないかと思います。結果的に各実行環境の開発者が少なくなり、品質にばらつきが出てしまうのではないかと予想するためです。
とは言え、純粋に仕様だけ見ていれば、Shenは面白いかなと思います。引き続きウォッチはしていこうかな。