0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Haskell] free-game(あるいはGloss)のスプライト定義をいい感じに書く方法

0
Posted at

free-gameはGlossを参考して作られた2Dグラフィックスライブラリで、GlossライクなDSL風の構文と、実装にFreeモナドを使用していることが特徴。

元々はfree-gameをラップするDSL的なものを作ろうと思っていたのだけど、構文を見た結果、わざわざ自前でパーサーを書いてDSL化まではしなくていいと判断。
ただ、スプライトの定義式がいまいち気に入らない。

font <- loadFont "examples/VL-PGothic-Regular.ttf"

-- 色と座標を指定してテキストスプライトを生成
translate (V2 300 300) $ color black $ text font 30 "A"

こんな風にスプライトの座標変換関数から逆算して書かないといけないので、いまいち読みづらい。
Haskellの構文上しょうがないのだけど、どうにかDSLらしくいい感じに書けるようにしたい。

使用環境

  • GHC 9.6.7
  • free-game 1.2
  • Ubuntu 24.04.2 LTS(WSL上)

GHCの最新版では依存ライブラリのJuicyPixels-utilがビルドできないため、stackなどでバージョンを調整する必要があります。
なお、free-gameの関数はGlossと似ているので、本記事の内容はGlossの使用にも生かせると思います。

先に結論から

こう書くのが正解。

text font 30 "test" &
    color green &
    translate (V2 220 300)

base-4.8.0.0から、$演算子をflipした&演算子というものが実装されており、Data.Functionをインポートすることで使用できる。

以下、それ以外に試した方法について。

Stateモナド

rotateRS :: (Affine p, MonadState (p a) m) => Double -> m ()
rotateRS = modify . rotateR

rotateDS :: (Affine p, MonadState (p a) m) => Double -> m ()
rotateDS = modify . rotateD

scaleS :: (Affine p, MonadState (p a) m) => Vec2 -> m ()
scaleS = modify . scale

translateS :: (Affine p, MonadState (p a) m) => Vec2 -> m ()
translateS = modify . translate

thicknessS :: (Picture2D p, MonadState (p a) m) => Float -> m ()
thicknessS = modify . thickness

colorS :: (Picture2D p, MonadState (p a) m) => Color Float -> m ()
colorS = modify . color

blendModeS :: (Picture2D p, MonadState (p a) m) => BlendMode -> m ()
blendModeS = modify . blendMode

-- 使用例
(`execState` (text font 30 "test")) $ do
    colorS red
    translateS (V2 260 300)

スプライト変換用の関数をmodifyでオーバーラップしてStateモナドを使用する方法。
途中で型が変化しない(できない)点がメリットだが、putなどが途中で入るとステートを全部書き換えられてしまうのが難点。

型制約については既に呼び出し側で保証されているため、今回のケースには合わないと判断。

$をflipする

flip ($) (text font 30 "test") $
    color blue >>>
    translate (V2 240 300)

>>>はCategory用の演算子だが、->がCategoryなので関数合成にも使用できる。
ただし関数適用には別の方法を使用しないといけないので、いろいろと中途半端。

最後に

型制約の強い言語なのでなんとなくいい感じにできるかなと思っていたのですが、同じ処理でも記述のパターンがかなり多く、意外と可読性のライブラリ依存度が高いなと感じました。
「関数型言語だから何をやっても大丈夫!」とはいかないようです。

まあContとかあるしね…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?