Haskell、非常に近いものとして、またはgithub apiからコミットを取得する

遅すぎる-「今手に入れたから」

周りにモナドがあります

IO、状態およびリストは豊富

それらは人々が言うように、簡単です

しかし、私のプログラムはすべて抽象化されました!

たぶん-おー、

モナドでもあります

なぜ別の言語を使用する必要があるのですか?





再び、Haskellの非常識な熟練者であり、彼の実用性を証明するためのさらに別の試み。 エイジレスクラシック。

私はゴージャスな物語を語ろうとします(哀れことはしないでください) 、大ヒットのすべての必要なコンポーネントがあります(私は真剣に、だまされません) -馴染みのあるキャラクター、うまく設計された宇宙とオープンエンド(まあ...)



少し深刻さは決して痛いことはありません。 したがって、最初に、ユーモアのわずかなヒントなしで、このテキストを書くロジックを教えます。 Haskellに、痛々しいほど緊密で、信じられないほど実用的なタスクを実装したかったのです。 この問題を解決することの肯定的な結果は、このプログラミング言語の選択を支持する、自分自身、スキル、およびもう1つの議論を誇りに思う特別な理由を与えるでしょう。 実験的なタスクとして、githubのリポジトリへのコミットに関する情報を受信して​​処理することにしました。 実際には、github api-jsonの読み込みと解析に関する作業が含まれます。



私はそれを段階的に解決する必要があると信じているので、開始位置、つまりファイルシステム内の空のディレクトリから始めましょう。



モジュール作成



まず、目的に合わせて新しいモジュールを作成します。



cabal init
      
      





問い合わせを行うcabalはいくつかの質問をします。その結果、設定ファイルproject_name.cabalでモジュールが空白になります。 より美しくするために、 srcディレクトリをモジュールに追加し、設定で指定します



 executable project-name hs-source-dirs: src main-is: Main.hs
      
      





もちろん、 Main.hsを作成する必要があります)



次に、 依存関係の地獄に関するいくつかの言葉。 これは、進行状況の概要を説明するHaskellの痛ましいテーマです。 依存関係の問題を解決するためのいくつかのオプションがありますが、私たちは若いのでファッショナブルなものすべてが大好きなので、最新のcabal-1.18機能であるサンドックスを使用します。



実際には、使用するには、サンドボックスを初期化し、依存関係をインストールする必要があります



 cabal sandbox init cabal install --only-dependencies
      
      





将来的には、モジュールを構築するために、通常どおり、次のコマンドを使用できます



 cabal build
      
      





何かをデバッグしたいという切実な欲求があり、実際に、それが内部からどのように機能するかを確認したい場合(そして、ジャンルの法則によれば、そのような欲求は必然的に発生します)、コマンドによって作成されたサンドボックスでghciを実行できます



 cabal repl
      
      





空のカタログに対する恐怖はすべて終わったので、次に進みます。



http-conduit



解決する必要がある最初のタスクは、コミットに関する情報をjson形式でロードすることです。 実際、ソースは明らかですが、簡単なことはそこで終わります。 そのため、この段階では、 http-conduitパッケージを使用して、太陽に面したエドワードスノーマイケルスノイマンのオーサーシップを作成します。 全体として、コンジットはデータストリームを操作するための優れたソリューションです。 私はこれについてあなたによく話すことができそうにないので、 eaxという名前の男性のブログへようこそ。 少しだけ、周辺で説明します。



最初に、構成ファイルのbuild-dependsセクションに必要な依存関係を追加します。



 bytestring >= 0.10, conduit >= 1.0, http-conduit >= 1.9,
      
      





上記のコマンドでサンドボックスを更新します。



これで、心配せずにコードに進むことができます。 まず、生活を簡素化し、文字列を操作するために、 拡張機能を追加します



 {-# LANGUAGE OverloadedStrings #-}
      
      





必要なモジュールを接続します



 import Data.Conduit import Network.HTTP.Conduit import qualified Data.Conduit.Binary as CB import qualified Data.ByteString.Char8 as BS
      
      





すべてのjsonダウンロードコードは次のようになります



 main = do manager <- newManager def req <- parseUrl "https://api.github.com/../.." let headers = requestHeaders req req' = req { requestHeaders = ("User-agent", "some-app") : headers } runResourceT $ do res <- http req' manager responseBody res $$+- CB.lines =$ parserSink
      
      





私の知る限り、github apiにはUser-agentヘッダーが必要なので、リクエストを少し拡張する必要がありました。 メインアクションは最後の2行で実行され、jsonから応答を受け取ります。 なぜなら 結果はResourceTトランスフォーマーにラップされ、それを取得する関数はrunResourceTを使用して呼び出す必要があります。 応答本文を受信した後、jsonを解析するように設計されているストックに送信します。これは次のようになります。



 parserSink :: Sink BS.ByteString (ResourceT IO) () parserSink = do md <- await case md of Nothing -> return () Just d -> parseCommits d
      
      





成功した場合、ストックは受信したjsonを単純に解析し、画面に表示します(マジックのこの部分はparseCommits関数で非表示になります)。



エイソン



プログラマーの考え方を歪め続け、構文解析に進みます。 彼のために、 Aesonと呼ばれる非常に強力なパッケージを使用します。 実際、ここではすべてが非常に簡単ですが、習慣から外れてst迷するいくつかのポイントがあります。





したがって、最初にタイプを定義します。 jsonから炉に情報の一部を送信して、それらを気にすることはできません。 URL、ハッシュ、コミットメッセージのみを残します。



 import qualified Data.ByteString.Char8 as BS import Data.Aeson (FromJSON(..)) data CommitInfo = CommitInfo { message :: BS.ByteString } deriving (Show) data Commit = Commit { sha :: BS.ByteString, url :: BS.ByteString, commit :: CommitInfo } deriving (Show)
      
      





さらに、データ構造からjsonとフィールドを照合するために適用可能なファンクターを使用することは標準的ですが、だれもを欺いてGenericを使用します。



 {-# LANGUAGE DeriveGeneric #-} import GHC.Generics (Generic)
      
      





既存のデータ構造に汎用継承を追加します



 deriving(Show, Generic)
      
      





jsonからCommit&CommitInfoを作成する可能性を宣言するだけです



 instance FromJSON Commit instance FromJSON CommitInfo
      
      





フィニッシュラインまでわずか数歩、我々はほとんどそこにいます



 parseCommits :: BS.ByteString -> Sink BS.ByteString (ResourceT IO) () parseCommits rawData = do let parsedData = decode $ BL.fromChunks [rawData] :: Maybe [Models.Commit] case parsedData of Nothing -> liftIO $ BS.putStrLn "Parse error" Just commits -> liftIO $ printCommits commits
      
      







ご覧のとおり、デコードに戻すために遅延バイト文字列を作成する必要があります。 解析が成功した場合、 liftIOを使用て取得した値上げ、コンソールに表示します。



終わり



すべて、レッドカーペット、ファンファーレ、厳soleな夜の終わり。 完全な例はこちらにあります 。 このコードは、コンピューターサイエンスの理想の勝利の例ではないため、第一人者からのコメントを歓迎します。 他の皆が何かを学んだか、少なくともそれを楽しんで、Haskellの世界に近づいたことを願っています。 力があなたと共にありますように!



All Articles