「48時間でSchemeを書こう/IOプリミティブの作成」の版間の差分
削除された内容 追加された内容
Semi-Brace (トーク | 投稿記録) M added Category:48時間でSchemeを書こう using HotCat |
Semi-Brace (トーク | 投稿記録) M編集の要約なし |
||
1 行
私たちのSchemeは外部の世界と未だに対話することが出来ません。もし何かしらのI/Oの機能があればとてもよいでしょう。同様に、私たちがインタプリターを起動する度に、本当に関数の中で長ったらしい記述をするより、コードが書いてあるファイルを読み込めて、それを評価できたらよいでしょう。<!-- TODO: syntaxhighlight -->
必要になる、まず最初のことは、LispVal のための新しいコンストラクタです。PrimitiveFuncsはIOモナドを含まない特別な型を持っています。ですので、他のIOを用いることができません。私たちは、IOを用いることができる専用のコンストラクタが必要です。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
この中で、私たちは、同様に [http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.6.1 port] という、Schemeのデータタイプの為のコンストラクタを定義します。私たちのIO関数の多くは、これらの一つを取って、読み書きされます。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
[http://www.haskell.org/onlinereport/io.html#sect21 Handle] は、基本的な portの Haskell notionで、openFIleおよび類似のIOアクションによって返され、貴方はそれへ読み書きを行うことが出来ます。
完全を期すために、私たちは新しいデータ型のために、showValメソッドを定義する必要があります。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
これは、きっと、REPL機能を適切にし、portを返す関数を使うときに、クラッシュしないようにしてくれるでしょう。
私たちは同様に applyを修正し、IOFunc が扱えるようにします。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
[http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.6.4 load]をサポートするため、私たちのパーサーをマイナーチェンジする必要があります。普通、Schemeファイルは複数の定義を含んでおり、パーサーにも、幾つかの評価をサポートしたり、あるいは空白によって分割することを追加しないといけません。そして、同様に扱い時のエラーも必要になります。私たちは、実際のパーサーがパラメーターを取れるような基礎的なreadExprを作ることによって、殆どの存在する基盤を再利用することが出来ます。
<syntaxhighlight lang="haskell" highlight="1-2,6-7">
</syntaxhighlight>
▲ readExprList = readOrThrow (endBy parseExpr spaces)
再び、readExpr及びreadExprListの両方を、新しく命名されたreadOrThrowが特別化されたものとして考えます。私たちは、自分たちのREPLを単純な評価として読み込みます。私たちは、readExprListを、loadの中から、プログラムの中から読み込むために使うでしょう。
次に、ただ存在するprimitiveリストみたいに構成された、IO primitvesの新しいリストが必要になるでしょう。
<syntaxhighlight lang="haskell">
ioPrimitives :: [(String, [LispVal] -
ioPrimitives = [("apply", applyProc),
("open-input-file", makePort ReadMode),
48 ⟶ 49行目:
("read-contents", readContents),
("read-all", readAll)]
</syntaxhighlight>
ここでは、違いは型のシグネチャの違いです。残念なことに、私たちは、存在するprimitiveのリストを使うことが出来ません。というのも、リストは型の違いによる要素に含めることができないからです。私たちは同様に、primitiveBindingsの定義を新しいprimitivesに新しく追加するよう編集する必要があります。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
私たちは、コンストラクターの引数を取るためにmakeFuncを作り、そして今や過去のまっさらなprimitivesにioPrimitiveのリストを追加してmakeFuncを呼び出します。
今、私たちは実際の関数を定義し始めています。applyProcはapplyのまわりを薄く包み込み、解きほぐされた引数のリストから、applyが期待しているものへと反応します。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
makeProtはHaskellの関数であるopenFileを包み込み、右の型にコンバートし、そしてPortコンストラクタの中で返り値をラップします。これはIOMode、open-input-fileの為の、ReadMode及びWriteModeに、部分的に適応します。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
ClosePortは同様に、Haskellの同等の手続きと、同じhCloseをラップします。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
Schemeのために、LispValに適切に切り替えるために、(ビルドインされたreadと、名前がコンフリクトしないように避けられた)readProcは、hGetLineをラップし、そしてparseExprの結果に送ります。▼
<syntaxhighlight lang="haskell">
▲ Schemeのために、LispValに適切に切り替えるために、(ビルドインされたreadと、名前がコンフリクトしないように避けられた)readProcは、hGetLineをラップし、そしてparseExprの結果に送ります。
readProc :: [LispVal] -> IOThrowsError LispVal
</syntaxhighlight>
▲ readProc [Port port] = (liftIO $ hGetLine port) >>= liftThrows . readExpr
"hGetLine port"が如何に
writeProcはLispValをストリングにして、特別なポートにコンバートして書き直すということです。
121 ⟶ 128行目:
そして、新しいrunOne関数によって、mainを変化させましょう。私たちはもはやコマンドライン引数の間違った値を扱うために、三つ目の節が必要となりましたので、私たちはif文を簡単に、ここに追加しましょう。
<syntaxhighlight lang="haskell">
</syntaxhighlight>
[[カテゴリ:48時間でSchemeを書こう]]
|