Posted at
LispDay 20

ShenのチュートリアルをやってFizzBuzzを書くまで試した

More than 5 years have passed since last update.


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は面白いかなと思います。引き続きウォッチはしていこうかな。