Haskellで何がやりにくいって、やはり変数がないこと。変数はあると言われるかもしれないが、値を変更できないラベルを変数と呼ぶのは何か間違ったことなのではないかと思う。
epub2to3_H.hs の 関数 fixHtml に
dOut = ruleTransformer transform dIn
という記述があり、一般的にはdOutを変数と呼ぶであろう。しかしこれは文法的にも動作的にも引数のない関数の定義であり、「ruleTransformer関数の結果をdOutに代入」ではなく「dOut関数の定義は云々」となる。
同じ部分をScala epub2to3_S.scala では
val d = new RuleTransformer(new FixRewriteRule())(rootE)
としている。dはvalなのでこれは確実に変数であるが、値の変更できないラベルでもある。この部分は一回しか通らず、またここに記述されたタイミングで呼ばれる必要もないので、
def d = new RuleTransformer(new FixRewriteRule())(rootE)
と関数として書いても大差ない。大差はないけど、valとdefはやはり変数と関数で意味はかなり違う。ともかくここでは似たような結果が得られる。valのインスタンスは1つだけどdefは呼ばれる度にインスタンスが作られる。
例えば fixZip の
val zos = new ZipOutputStream(fos)
を
def zos = new ZipOutputStream(fos)
なんてしたら破綻する。ストリームのインスタンスは1つだからこそ適切に扱える。やはりScalaにおいては変数は普通に変数。Javaとの互換性ということもあるだろうし。
この Scalaでの zos に相当するナニガシはHaskellにはない。そもそもScalaの方はストリームを渡して書いて貰って云々という動きなのに対して、Haskellではバイナリ文字列を渡してバイナリ文字列を受け取るという富豪な動きになる。ならざるを得ない。スタックやヒープが心配になる程度には貧乏性な私。
Pythonのepub2to3_P.psではライブラリの動作から、Haskellと同じようににバイナリ文字列を渡してバイナリ文字列を受け取るという富豪な動きにした。プログラミングパラダイムがどうしたよりはライブラリが、この件ではZIPファイルを扱うライブラリがどう作られているかが大きい。
ちなみに、このサンプルプログラムにおいて、Scalaはvarを使わない、Pythonは変数の再代入を行わないという縛りを行っている。ただし、Pythonでは配列のappendを多用している。Pythonにも関数型言語由来のmap関数やreduce関数はあるけれど、あまりにもPythonらしくなく、推奨もされないので普通にfor文を使っている。
2014.07.14
OSTRACISM CO.
OSTRA / Takeshi Yoneki