Help us understand the problem. What is going on with this article?

Haskell入門者がライブラリを触っちゃう!?

More than 1 year has passed since last update.

はじめに

これはAdventCalendar2017の22日目の記事です。
はじめまして。@brackss1です。
「すごいH本」を読み終わって何か作りたくなりました。そこで目を付けたのがWeb開発です。有名なWebFrameworkといえば Ruby on Rails, Node.js, Go on Revel, Django などが思いつきますが、Haskellにも Yesod, Spock などwikiに紹介されているだけでも14個のライブラリがあります。この中から今回はscottyを使いました。

環境

Haskell使うにはStack(パッケージ管理)で神。
エディターはVSCodeを使いました。Haskero拡張インストールしてinteroをセットすれば補完できるのでなんとかなります。
せっかくなのでインターネット上にあっぷしたい。お手軽なのはGitHub.IOですが静的サイトしか公開できない(DBが使えない)ので、Herokuを使います。無料で使えるので選びました。(だいぶ制限されますが)

Herokuについてちょっと

PaaS(Platform as a Service)。git&herokuコマンドでコマンドラインで操作できるので楽しい。以上。

ライブラリ…

WebAppの要素は主に3つあると思って…
1. Routing
2. HTML+CSS+Javascript
3. DataBase
YesodのようなフルスタックのWebFrameworkもありますが、Haskellはライブラリが豊富なのでこれらに対応するものが独立してあります。

Routing

WebサイトのURLとそこで表示するページを結びつけます。はじめに書いたようにscottyを使います。詳しいドキュメントはHackageでも見てください。
サンプルは実行してhttp://localhost:3000 にアクセスしてください。

sample
{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty

main :: IO()

main = scotty 3000 $ do
  get "/" $ html "<h1>Hello!</h1>"

ただHello!と表示されます。POST通信はログイン機能で披露したいので、HTMLファイルのパースを紹介した後にしたいと思います。
外部鯖(ここではHeroku)に公開したい場合は次のようにします。

getPort :: Maybe Int -> Int
getPort (Just n) = n
getPort Nothing = 3000

port <- lookupEnv "PORT"
scotty (getPort $ read <$> port) $ do 

HTMLパース&レンダリング

HTML+CSS+Javascriptの書き方はこの記事では書きません。
実際に表示するためのhtmlをパース&レンダリングします。数あるライブラリの中でedeを選択しました。aesonのJSONパーサが利用できるので。

index
<!DOCTYPE html>
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <h1>{{ title }}</h1>
        <ol>
            {% for food in foods %}
            <li>{{ food.value }}</li>
            {% endfor %}
        </ol>
    </body>
</html>
parse&render
{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty
import Text.EDE
import Data.Aeson
import Data.Text(Text)

main :: IO()

main = do

  path <- eitherParseFile "./html/index.html"

  let env = fromPairs [("title" :: Text) .= ("好きな食べ物TOP3" :: Text)
                      ,("foods" :: Text) .= (["フォー","餃子","石焼ビビンバ"] :: [Text])]

  let txt = either error id $ path >>= (`eitherRender` env)

  scotty 3000 $ do
    get "/" $ html txt

自作のデータ型を使いたい場合は…

data Food = Food {name :: Text, from :: Text}

foods :: [Food]
foods = [Food "curry and rice" "India",
         Food "hamburger" "America",
         Food "ramen" "China"]
instance ToJSON Food

ToJSONのinstanceにすればOKです。

DBをさわる

リレーショナル・データベースとして有名なのはMySQLとPostgreSQLですが、Herokuで使えるのが後者だけということなのでそれを選びました。ライブラリは@khibinoさんのhaskell-relational-record(以後HRRと略)を使います。(cabalファイルに書くときはrelational-queryです。とりまsqlite3をインストールしておいてください。

sample
import Data.Int(Int32)
import Database.Relational.Query

hello :: Relation () (Int32, String)
hello = relation $ return $ value 0 >< value "Hello"

world :: Relation () (Int32, String)
world = relation $ return $ value 0 >< value "World"

helloworld :: Relation () (Int32, String, String)
helloworld = relation $ do
    h <- query hello
    w <- query world
    on $ h ! fst' .=. w ! fst'
    return $ (,,) |$| h ! fst' |*| h ! snd' |*| w ! snd'

main :: IO ()
main = putStrLn $ show helloworld ++ ";"
command
stack build&stack exec sample-exe | sqlite3
result
0|Hello|World

公式ドキュメントほぼまんまですw。ここでは仮想のDBを作成して作業しています。
次は実際のDBをいじりましょう!といきたいですが…
TemplateHaskellの知識が少々必要になります。
残念ながらWriterにはこの知識がないのでテキトーなことは言えません。
実際にDBを操作するときは、defineTableマクロでもろもろ自動導出してくれるのでかなり楽っぽいです。先ほどの操作を実際に発行するときはrunQueryで実行します。
以上です。ここらへんはぼくもちんぷんかんぷんです(すいません…)

おわり

とりあえず「Haskellでなんかしたい」みたいなのは叶えられました。
ここではそれぞれのライブラリについて断片的に紹介して終わりにしました。
haskell-jpのslackで質問に答えくださったみなさんには感謝しています。
コメントorマサカリは https://twitter.com/seatofhorse まで。
おわり。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away