👻

IHP(haskell)でルーティング追加してみた

2023/11/26に公開

IHPとは?

僕も触りたてなのでchatGPTに聞いてみたです。

IHP(Integrated Haskell Platform)は、Haskellプログラミング言語向けのウェブフレームワークです。Haskellは強力で型安全な静的型付けのプログラミング言語であり、IHPはこの言語を使用してウェブアプリケーションを開発するためのフレームワークとして提供されています。

IHPは、以下の特徴を持っています:

  1. 完全なスタックの統合: IHPはデータベースからフロントエンドまでの完全なスタックを統合しており、これによりアプリケーションの一貫性を高めます。

  2. 型安全なテンプレート: Haskellの型システムを利用して、テンプレート内で型安全なコードを記述できます。これにより、ランタイムエラーのリスクを低減できます。

  3. コードジェネレーション: IHPはコードジェネレーションを活用して、開発者が簡単かつ迅速にアプリケーションを構築できるようにサポートします。

  4. CRUD自動生成: データモデルを定義するだけで、IHPは基本的なCRUD(Create, Read, Update, Delete)操作のためのコードを自動生成します。

  5. ライブリロード: 開発中にコードの変更が即座に反映されるため、効率的な開発が可能です。

IHPは、Haskellを使用することで強力な型システムと関数型プログラミングの利点を活かし、ウェブアプリケーションの開発をより安全かつ効果的に行えるようにします。

とりあえず、Haskellの WEBフレームワークということ。

本題

前提としてIHPはMVCです。

ステップ

  • Viewファイルの新規作成
  • Controllerファイルの新規作成
  • Routes.hsの編集
  • Types.hsの編集
  • FrontController.hsの編集

Viewファイルの新規作成

ほぼ初期テンプレートのままですが、以下のような感じで作ります。
構文はHSXというらしいです。
JSXのHaskell版って感じみたいです。

module Web.View.Static.Hello where
import Web.View.Prelude

data HelloView = HelloView

instance View HelloView where
    html HelloView = [hsx|
         <div style="background-color: #657b83; padding: 2rem; color:hsla(196, 13%, 96%, 1); border-radius: 4px">
              <div style="max-width: 800px; margin-left: auto; margin-right: auto">
                  <h1 style="margin-bottom: 2rem; font-size: 2rem; font-weight: 300; border-bottom: 1px solid white; padding-bottom: 0.25rem; border-color: hsla(196, 13%, 60%, 1)">
                      IHP
                  </h1>

                  <h2 style="margin-top: 0; margin-bottom: 0rem; font-weight: 900; font-size: 3rem">
                      Hello world
                  </h2>

                  <p style="margin-top: 1rem; font-size: 1.75rem; font-weight: 600; color:hsla(196, 13%, 80%, 1)">
                     Your new application is up and running.
                  </p>

                  <p>
                      <a
                          href="https://ihp.digitallyinduced.com/Slack"
                          style="margin-top: 2rem; background-color: #268bd2; padding: 1rem; border-radius: 3px; color: hsla(205, 69%, 98%, 1); text-decoration: none; font-weight: bold; display: inline-block; box-shadow: 0 4px 6px hsla(205, 69%, 0%, 0.08); transition: box-shadow 0.2s; transition: transform 0.2s;"
                          target="_blank"
                      >Join our community on Slack!</a>
                  </p>

                  <a href="https://ihp.digitallyinduced.com/Guide/your-first-project.html" style="margin-top: 2rem; background-color: #268bd2; padding: 1rem; border-radius: 3px; color: hsla(205, 69%, 98%, 1); text-decoration: none; font-weight: bold; display: inline-block; box-shadow: 0 4px 6px hsla(205, 69%, 0%, 0.08);  transition: box-shadow 0.2s; transition: transform 0.2s;" target="_blank">
                     Learn the Next Steps in the Documentation
                  </a>
              </div>
         </div>

         <div style="max-width: 800px; margin-left: auto; margin-right: auto; margin-top: 4rem">
              <img src="/ihp-welcome-icon.svg" alt="/ihp-welcome-icon" style="width:100%;">
              <p style="color: hsla(196, 13%, 50%, 1); margin-top: 4rem">
                 You can modify this start page by making changes to "./Web/View/Static/Welcome.hs".
              </p>
         </div> 
|]

Controllerファイルの新規作成

PostsContorollerを作ります。
先ほど作ったViewファイルをインポートして、renderの後に宣言します。

module Web.Controller.Posts where
import Web.Controller.Prelude
import Web.View.Static.Hello

instance Controller PostsController where
    action PostsAction = render HelloView -- ←ここ

Routes.hsの編集

先ほど作成したControllerを追記します

module Web.Routes where
import IHP.RouterPrelude
import Generated.Types
import Web.Types
-- Generator Marker
instance AutoRoute StaticController
instance AutoRoute PostsController -- ←ここ

Types.hsの編集

以下のように定義します。
作成したController内で宣言したaction(PostsAction)を定義しましょう。
Routes.hsでAutoRouteを宣言すると、Types.hsを見にいくようです。

module Web.Types where

import IHP.Prelude
import IHP.ModelSupport
import Generated.Types

data WebApplication = WebApplication deriving (Eq, Show)

data StaticController = WelcomeAction deriving (Eq, Show, Data)

data PostsController
    = PostsAction
    deriving (Eq, Show, Data) -- ←ここ

FrontController.hsの編集

ここで、ブラウザからのリクエストと返却するactionをマッピングしてるっぽいです。

module Web.FrontController where

import IHP.RouterPrelude
import Web.Controller.Prelude

import Web.View.Layout (defaultLayout)

-- Controller Imports
import Web.Controller.Static
import Web.Controller.Posts -- ←ここ

instance FrontController WebApplication where
    controllers = 
        [ 
         startPage WelcomeAction
        , parseRoute @PostsController -- ←ここ
        ]

instance InitControllerContext WebApplication where
    initContext = do
        setLayout defaultLayout
        initAutoRefresh

さいごに

フレームワークなので、最初は暗記ゲーみたいな感じでちょっと疲れます。
ただ、MVCなのでFrontController.hsあたりとか、RailsとかLaravelにも似たような機構あったなぁと思ったので、すでに持っている知識と上手く繋げて考えられる感覚が持てれば割と認知負荷は少ないかもしれません。
いろいろ触ってみると、過去に経験した内容とぼんやり似ている部分が見つかってくる楽しさはありました。

SPAの文脈だとJSXは合理的だなぁと思うのですが、IHPはMPAっぽいのでHSXで書けることのメリットってなんなんだろう?と少し疑問には感じしました。
ただ、テンプレートエンジンのような構文ではない、というだけでもメンタルモデルが一緒という意味では認知負荷が下がるメリットはある(DX)かなと思ったりした次第です。

Discussion