仕組みがわかりにくかった。
そこで、次のような質問をChatGPTにしてみた。
Q1:piNewUserは、どういうふうに使い方をするのですか?
Ans: let ins = HRR.typedInsert User.tableOfUser piNewUser がポイント
typedInsert :: Table User -> Pi User NewUser -> Insert NewUser
第1引数 User.tableOfUser → 「Userテーブルそのもの」
第2引数 piNewUser → 「NewUser から User のカラムに対応付ける射影」
let ins = HRR.typedInsert User.tableOfUser piNewUser
u' = u { nuPassword = dec hashed } ins は INSERT 文のひな型
DHR.runInsert conn' ins u' と実行すると、
u' :: NewUser の値(ユーザー名とハッシュ済パスワード)を
piNewUser に従って User テーブルの name, password カラムに対応付け
実際に DB へ INSERT
Q2: 要するに、手順を最初にくみたてて、最後に、runInsertで実行するのですね。
Ans: はい、その理解でバッチリです!
まとめると:
piNewUser で「どう射影するか」を定義(テーブルと型の橋渡し)。
typedInsert で「INSERT文のひな型」を作成。
runInsert で「実際の値」を流し込んでDBに送信。
※ Haskellは、手続き型のように「値を受け取りながら逐次SQL発行」というやり方はあまりせず、まず「設計図を型安全に作る → 値を流し込む」という 宣言的な流れ
型安全性が高い:SQL文やカラム名の間違いをコンパイル時に検出できる
SQLインジェクション対策が組み込みである:値を直接文字列連結しないので安全
トランザクション管理が明確:withTransaction の中で設計図に値を流し込むので、途中でエラーがあればロールバック可能
コードの再利用性が高い:射影(piNewUser のような定義)を作っておくと、INSERTや SELECTで同じマッピングを使い回せる
などの利点があるらしい
Q3:makeRecordPersistableDefault ''NewUser はどういう意味?
Ans:NewUser を Relational-Record の「DBに入出力できる型」として登録する宣言、Template Haskell によって必要な型クラスインスタンスを自動生成している
「この型をSQLに流せるようにしますよ!」と最初に宣言しておく儀式 みたいなもの
Q4:Template Haskellとは?(P340にも説明はありますが)
Ans:「Haskellプログラム自身を生成する Haskellプログラム」を書ける
Q5:自動生成ですね。DjangoやRuby on Railsなどでも、DB初期化で似たような処理あったような気がしますね.
Ans:まさに Django や Ruby on Rails と同じ発想です。
Rails/Django = 実行時に自動生成・マッピング
Haskell(TH) = コンパイル時に自動生成・型安全にマッピング
Q6:user <- DHR.runQuery conn q name >>= DHR.listToUniqueのDHR.listToUniqueの働きは?
Ans:[] の場合 → Nothing を返す、[x] の場合 → Just x を返す、[x, y, ...] の場合 → エラーか Nothing を返す。「DB側にユニーク制約があるとは限らない」設計を想定。
Q7:checkHash user | validated = Just user | otherwise = Nothingの意味は?
Ans: if ... else if ... else if ... elseのイメージで、最初にtrueになったところが checkHash userの値。ガードの機能(p62で既習)
0 件のコメント:
コメントを投稿