未来を創る、テックコミュニティー

代表技術通信~Get Programming with Haskell⑥

草場代表
2020/11/30

こんばんは。代表の草場です。

Haskell触ります。「Get Programming with Haskell」についてです。次は、レッスン5です。

レッスン5. クロージングと部分適用

レッスン5を読んだ後は

ラムダ式での値のキャプチャ
クローザを使用して新しい関数を作成する
部分的なアプリケーションでこのプロセスを簡素化

が理解できます。

関数型プログラミングの最後の重要な要素であるクロージャについて。

クロージャは、ラムダ関数とファーストクラス関数を持つことの論理的な結果です。これらのラムダ関数とファーストクラス関数を組み合わせてクロージャを作成することで、動的に関数を作成することができます

慣れるのに最も時間がかかります。

次のことを考えてます。URLを取得するget-Price関数と、Webサイト固有の価格抽出関数があるとします。

getPrice amazonExtractor url

1,000個のURLからアイテムを抽出する必要がある場合、すべてamazonExtractorを使用するとどうなるか?その場でこの引数をキャプチャする方法があるか?

 5.1. CLOSURES-関数を使った関数の作成

レッスン4では、ifEvenという関数を定義しました。ifEven の引数として関数を使用することで、計算のパターンを抽象化することができました。次に、関数 ifEvenInc、ifEvenDouble、および ifEvenSquare を作成しました。

ifEvenInc n = ifEven inc n
ifEvenDouble n = ifEven double n
ifEvenSquare n = ifEven square n

関数を引数として使うことで、コードをすっきりさせることができました。

しかし、プログラミングのパターンが繰り返されています。欲しいのは ifEvenX 関数をビルドする関数です。これを解決するには、genIfEven と呼ばれる関数を返す新しい関数を作成します。関数を渡してラムダ関数を返し、渡された関数 fをラムダ関数の中に取り込みます。ラムダ関数の内部に値を取り込むことをクロージャと呼びます。

これをよりよく理解するために、genIfEvenを使ってifEvenInc関数を作成する方法を見てみましょう。APIで使用するURLを構築するためにクロージャを使用する実際の例に移ります。

5.2. API の URL を生成する

データを取得する最も一般的な方法の 1 つは、HTTP リクエストを使用して RESTful API を呼び出すことです。最も単純なタイプのリクエストは GET リクエストで、別のサーバーに送信する必要のあるすべてのパラメータが URL にエンコードされています。この例では、各リクエストに必要なデータは以下の通りです。

ホスト名
リクエストしているリソースの名前
リソースのID
あなたのAPIキー

ここに基本的なgetRequestURL ビルダーを使い、これらのパーツからURLを簡単に構築できます。

getRequestURL host apiKey resource id = host ++
"/" ++
resource ++
"/" ++
id ++
"?token=" ++
apiKey

この関数の奇妙な点としては、引数の順番が使用する順番や URL 自体に表示される順番と同じではないということです。クロージャを使いたいときはいつでも、引数を最も一般的なものから最も一般的でないものへと順番に並べたいものです。この場合、各ホストは複数のAPIキーを持つことができ、各APIキーは異なるリソースを使用し、各リソースはそれに関連付けられた多くのIDを持つことになります。ifEven を定義した場合も同じことが言えます。渡す関数は膨大な範囲の入力で動作するので、より一般的で、引数リストの最初に表示されるべきです。

基本的なリクエスト生成関数の概要がわかったので、それがどのように動作するかを見ます。

GHCi> getRequestURL "http://example.com" "1337hAsk3ll" "book" "1234"

"http://example.com/book/1234?token=1337hAsk3ll"

これは素晴らしい一般的な解決策ですが、チーム全体で多くのホストに問い合わせをすることになるので、 チームのほぼすべてのプログラマが数台のホストのデータに注目することになってしまいます。リクエストのたびにプログラマが手動で http://example.com を入力しなければならないため、エラーが発生しやすくなります。

必要なのは、誰もが自分のためだけにリクエストURLビルダーを生成できる機能です。これに対する答えはクロージャです。

exampleUrlBuilder = genHostRequestBuilder "http://example.com"

example.com という値を渡すと、ホストをキャプチャし、残りの 3 つの引数だけを必要とする名前のない新しい関数が作成されます。exampleUrlBuilder を定義する際には、無名関数に名前を付けます。リクエストしたい新しい URL があればいつでも、そのためのカスタム関数を簡単に作成できるようになりました。

この関数を GHCi にロードして、コードをどのように単純化するかを見ます。

GHCi> exampleUrlBuilder "1337hAsk3ll" "book" "1234"
"http://example.com/book/1234?token=1337hAsk3ll"

exampleUrlBuilder を呼び出すたびに API キーを渡すため、面倒です。これを解決するために別のクロージャを使用することもできます。exampleUrlBuilder 関数と apiKey の両方をジェネレータに渡さなければなりません。

genApiRequestBuilder hostBuilder apiKey = (\resource id ->
hostBuilder apiKey resource id)

ここで興味深いのは、引数としての関数と戻り値としての関数の両方を組み合わせていることです。クロージャの中には、必要となる特定の関数のコピーと、取得する必要のある API キーが入っています。

myExampleUrlBuilder = genApiRequestBuilder exampleUrlBuilder "1337hAsk3ll"

これを使って、さまざまなリソース/IDのコンボ用のURLを素早く作成することができます。

GHCi> myExampleUrlBuilder "book" "1234"
"http://example.com/book/1234?token=1337hAsk3ll"

 

この記事を書いた人
草場代表
エディター