One-liner in Haskell

Haskellを現場言語にするために、こんなものを作ってみました。

hoe: Haskell One-liner Evaluator
(名前には深い意味はありません。)

Haskellワンライナーをやろうという誰得なツールです。誰得ですが、ワンライナーでも、型があると便利なんではなかろうか、型を元にユーザの望みの動作が大体決定できるんではなかろうか、という発想を元に作られました。

Haskellワンライナーは、ghc -e でも評価できますが、これは (Show a) => a か、 (Show a) => IO a な型しか評価できません。hoeでは、String -> String など、もっと色々な型を評価できます。そして、その型に応じていい感じの動作が自動的に選択されます。

例えば、idを入力すると、入力がそのまま出力されます。

$ cat tmp
Hello, Haskell World!
$ hoe 'id' tmp
Hello, Haskell World!

そうです。もうcatを作るのに、
http://d.hatena.ne.jp/nobsun/20100820/1282270092
こんなプログラムを書く必要は無いんです。

文字処理

Char -> Char なら、文字列全体に与えられた関数を適用します。

$ cat tmp | hoe 'toUpper'
HELLO, HASKELL WORLD!

大変単純明快です。

[a] -> [a] ほか

基本的にhoeは、行指向の振る舞いをします。
[a] -> [a] は、[String] -> [String] にも String -> String にもマッチしますが、このような場合は [String] -> [String] が選択されます。例えば、take 2 なら、先頭から2行を取ってくる処理になります。

$ cat tmp
Hello,
Haskell
World!
$ cat tmp | hoe 'take 2'
$ hoe 'take 2' tmp
Hello,
Haskell

また、sort なら、行をソートする処理になります。

$ cat tmp | hoe 'sort'
Haskell
Hello,
World!

大変シンプル。

String -> String の関数を書いた場合も、行ごとの処理になります。

$ cat tmp | hoe '("> "++)'
> Hello,
> Haskell
> World!

行番号

Int -> String -> String などの関数を書けば、第一引数に行番号が入ります。

$ cat tmp | hoe '\ln s -> show ln ++ "> " ++ s'
1> Hello,
2> Haskell
3> World!

先頭に行番号を書いたりするのも、このとおり。

(Int, String) を返せば、第一引数でソートした結果が出力になります。

$ cat tmp | hoe '\_ s -> (length s, s)'
Hello,
World!
Haskell

これは、行を長さでソートします。

関数のlift/join

行ごとではなく、文字ごとに処理したい場合もあります。

たとえば、drop 2 と書くと、先頭2行を落とす処理になりますが、各行2文字落とす処理が書きたいとしましょう。これは

$ cat tmp | hoe 'map $ drop 2'
llo,
skell
rld!

のように、mapを用いればできますが、これはめんどくさい。そこで、与えられた関数を [String] -> [String] ではなく、 String -> String と解釈する --join (-j) オプションを用意しました(現在の実装は極めていい加減なので、型によってはうまく動かない時もあるかもしれませんし、joinじゃなくてliftするかもしれません)。これを用いると、

$ cat tmp | hoe -j 'drop 2'
llo,
skell
rld!

と書けます。

モジュール自動import

hoeは、独断と偏見で選別したよく使われるライブラリを、自動的にimportしています。上での例でのtoUpperが装飾なしで使えていたのはこのおかげです。

なので、例えば行の長さでのソートは

$ cat tmp | hoe 'sortBy $ comparing length'
Hello,
World!
Haskell

このようにも書けます。

inplaceモード

  • i を付けると、与えたファイルを書き換えます。

例えば、

$ hoe -i 'toUpper' *

とやると、すべてのファイルを大文字にします。-iオプションに文字列を渡すと、

$ hoe -i.bak 'toUpper' *

元のファイルに.bakを付けたファイル名でバックアップが作成されます。安心です。

値評価モード

当然ですが ghc -e でできる単なる (Show a) => a や、(Show a) => IO a の評価もできます。

$ hoe '2^100'
1267650600228229401496703205376
$ hoe 'pi*50^2'
7853.981633974483

電卓としても使えますね。

$ hoe 'forM [1..10] $ \i -> writeFile ("tmp."++show i) ""'

ゴミファイルの生成もお手の物。

あとがき

という訳で、誰得ツールを作ったお話でした。
超適当な実装で、まだまだ全然機能がありませんが、
ご意見ご感想などあればどしどしご送信下さい。