Haskell + SDL2 + Yampa

githubha_sdl をアップしたのは 2023年の6月だったやうだ(githubで確認した)
これは 縦書きや横書きで 文章(日本語や英語)をかけるだけでなく その場で簡單な計算をしたり コードを書いて實行したり 何なら ゲームまで立ちあげられてしまふ といふやうなエディタをめざしたものだった
まだまだ 改良しなければならないし ゲーム機能も 不完全だが 縦書きで文章を書く機能などは それなりに快適に動作してゐる
その後 Androidアプリをつくる道を發見し obelisk なるものを使って 歴史の年代並べ替へアプリ obRekisi や 一應ストーリーのある 脱出ゲーム的アプリ obHen をつくるのに熱中してゐた
ここで自分は FRP (Functional Reactive Programming) といはれる プログラム技法の すごさや 面白さを体感することになった
ゲームつくるなら FRPだな〜 と FRPの詳細など よく分かってもゐないくせに なんとなく實感してゐる次第だ

ただ obelisk で作成できたアプリは描画がとことんシンプルで ボタンを押したり タイマーが進むと ブラウザ画面がすばやく更新される といふもので キャラクターが縦横無尽に動きまはるやうなアクションを描くのは 難しい (まあ そもそも 描かうとも思ってゐないのだが)
それでも HTMLのcanvas要素にアクセスしたり できればSDL2 (Simple DirectMedia Layer 2)などを使って作ったアプリを Androidに移植できたらいいな〜 などとは思ってゐた

私がcanvasを使ってつくったものは fi で これは私のホームページのトップに居座ってゐる
これは Haste コンパイラーを用いて作ったものだが これなんかをAndroidでも動くやうにできれば Androidアプリの幅といふか つくれるものの種類も廣がるんぢゃないか などと思ってゐる

ただ obeliskcanvasを使ふと パソコンのブラウザ上では動作できたのだが Android用にコンパイルして インストールすると 画面が真っ白になって 固まってしまふのだった
今でも その謎は解けてゐない (そもそもobeliskcanvasを使ふのに とても試行錯誤し ブラウザで動いたときは 本當に感動したものだった・・・その後に絶望が待ってゐるのだが・・・)

そして obeliskでSDL2を使ふといふのは もっとハードルが高いやうに思はれた
それについて言及してゐるやうな webページは見當たらないし obelisk が使用してゐる reflex といふFRPのパッケージ に SDL2との連携バージョンである reflex-sdl2 があるのだが 使ひ方が全然わからない

そんなこんなしてゐる時に見つけたのが magic cookies といふゲームで これが Haskell + SDL2 + Yampa でつくられてゐる とのことだった

The game is written in Haskell. For Android, there's a C and Java wrapper that merely invoke our Haskell main. For iOS, that part is done in objective C. It uses SDL2 for multimedia. The core uses the Functional Reactive Programming library Yampa.

Yampa? 何それ おいしいの? と思ひ 調べてみたら 何かおもしろさうなのだが どうやって使ふか分からない

なんとなく分かったことは 作りたいアプリの インプット(キーボードとかマウスの入力) と アウトプット(画面表示や音楽など) を明確に分けて インプットで得た値 を 「シグナル函數」なるものを使って アウトプットで使ふ値に變更する機能が Yampaのメイン機能 だ といふこと

Yampa の 重要な函數が reactimate といふもので

reactimate
:: Monad m     
=> m a   --Initialization action
-> (Bool -> m (DTime, Maybe a))   --Input sensing action
-> (Bool -> b -> m Bool)   --Actuation (output processing) action
-> SF a b   --Signal function
-> m ()

といふ型の情報を Yampa の example-code と 自分がつくった ha_sdl などと見比べること3日間(くらゐ)・・・

なんと Yampa を 自分がつくった ha_sdlに組み込んで動かすことに成功した!! (githubha_sdl_yampa としてアップした)

變更したのは 3つのファイルで まず ha_sdl/src/MyEvent.hs の

inputEvent :: S.StateT State IO Input
inputEvent = do
  st <- S.get
 ....

といふ部分を

inputEvent :: Bool -> S.StateT State IO (DTime, Maybe Input)
inputEvent _ = do
  st <- S.get
....

とし 同じ函數の最後部分

S.put nst
  when isToIns $ startTextInput (Rect 0 0 50 50)
  when isToNor $ stopTextInput
  return ninp

S.put nst
  when isToIns $ startTextInput (Rect 0 0 50 50)
  when isToNor stopTextInput
  return (1,Just ninp)

とした (returnの後だけ變更)
また

initInput :: S.StateT State IO Input 
initInput = return NON

といふ函數を新たに加へた
次に ha_sdl/src/MySDL/MyLoop.hs のモジュール名(ファイル名も)を變更し ha_sdl_yampa/src/MySDL/MyOutput.hs として

myLoop :: Renderer -> [Font] -> [Texture] -> S.StateT State IO ()
myLoop re fonts itexs = do
  st <- S.get
  let actSt = act st

  inp <- inputEvent 

  st' <- S.get
 ....

となってゐたのを

myOut :: Renderer -> [Font] -> [Texture] -> Bool -> Input -> S.StateT State IO Bool 
myOut re fonts itexs _ inp = do
  st <- S.get
  let actSt = act st

  st' <- S.get
 ....

のやうに變更し 同じ函數の最後の部分も

S.put nst 
  if inp==QIT then do 
    fileWriteR fpsSt nst
    fileWrite textPosFile (T.pack$unwords [show ((fps.act) nst),show ((tps.act) nst)])
    fileWrite jumpNameFile (jumpsToText njps)
    return ()
              else myLoop re fonts itexs

から

S.put nst 
  if inp==QIT then do 
    fileWriteR fpsSt nst
    fileWrite textPosFile (T.pack$unwords [show ((fps.act) nst),show ((tps.act) nst)])
    fileWrite jumpNameFile (jumpsToText njps)
    return True 
              else return False

のやうにした (變へたのは return の後と else の後だけ)
この上で (といふか 實際はいろいろ試行錯誤したのだが)
ha_sdl/src/MyApp.hs の

appMain :: IO ()
appMain =
  withMyInit $ do
    (fonts,sur,text,(fpos,tpos),dots,jumps) <- myLoad
    withMyVideo sur $
      \(renderer,itexs) -> do
        let newActive = initActive{tex=text,dts=dots,fps=fpos,tps=tpos}
            newAttr = initAttr{jmp=initJumping{jps=jumps}}
            newState = initState{act=newActive,atr=newAttr} 
        stopTextInput
        S.runStateT (myLoop renderer fonts itexs) newState

appMain :: IO ()
appMain =
  withMyInit $ do
    (fonts,sur,text,(fpos,tpos),dots,jumps) <- myLoad
    withMyVideo sur $
      \(renderer,itexs) -> do
        let newActive = initActive{tex=text,dts=dots,fps=fpos,tps=tpos}
            newAttr = initAttr{jmp=initJumping{jps=jumps}}
            newState = initState{act=newActive,atr=newAttr} 
        stopTextInput
        S.runStateT (reactimate 
                        initInput 
                        inputEvent 
                        (myOut renderer fonts itexs)
                        identity 
                    ) newState

とした (變へたのは S.runStateT の後だけ)
そして Yapma のライブラリを使へるやうに また 變更したモジュールを利用できるやうに

import FRP.Yampa (identity,reactimate)
import MySDL.MyOutput (myOut)
import MyEvent (inputEvent,initInput)

を ha_sdl_yampa/src/MyApp.hs に追加した
identity といふのは シグナル函數で 入力されたものを そのまま出力として返すものだ
inputEvent で取られた入力(Input) は そのまま myOut函數に適用されるので Yampanのシグナル函數の威力はほぼ使用しないものの
とりあへず ちょっとしたコードの變更で 今まで實現してゐたことが Yampa機能を實装した形で實現できたことになる
まあ 今は シグナル函數をほぼ利用しない シンプルなものだが シグナル函數をうまく利用すれば より高度なことも出来るやうになるんぢゃないか
なんて 勝手に期待してゐる

・・・ただ なんだらう こんなこと考へても仕方のないことなのかもしれないが
さきほどの magic cookies にしても obelisk にしても全然盛り上がってゐる気配がない
magic cookies をつくった KEERA STUDIOS は 洗練された技術を使ったことで賞もとってゐるやうだ
なのに ツイッターは 2022年から更新された形跡がない
obelisk も最後に更新されたのが 10ヶ月くらい前のやうだ
ただ その母体となる reflex は 今月更新されてゐるし 1ヶ月でのダウンロード數は 189もある
結構 利用されてゐるやうなのだ
Yampa は頻繁に更新され 利用されてゐるやうだ (10月8日更新 ダウンロード數178) なら もっと 利用してゐる人の情報があって良いものだと思ふのだが・・・
とにかく example がない
あっても 意味わかんない もしくは 古くて 今のものに應用がききにくい
とにかく Haskell を独自で始めて (他の言語に詳しいとかでもなく) いろいろ試行錯誤しながら ゲームを作りたい なんて思ってゐる人にとって
道がなさすぎる・・・あっても険しすぎる・・・
だいたい Haskellについて解説されてゐる記事は インストールとか 基本文法とかの 超キホンみたいで ただ計算をいじるだけの あまり面白くないもの か 新しく出たGHCコンパイラーの機能で遊んでみた とか モナディックななんちゃらが ファンクターで みたいな
専門用語バシバシで 言語拡張はあたりまへ
讀まうとしたら 10秒で 頭がハテナで埋めつくされる ・・・そんなんばっかじゃん
Haskellを使ひこなしてゐる人ほど 普及に力を入れて欲しいのに・・・
私みたいに Haskellを使ひこなせてゐない ただのHaskell好きが Yampaを導入する記事なんか書いてゐる・・・

ただ 私のやうな者でも 傳へられることがあるとしたら Haskell といふ言語は それほど内部を理解してゐなくても
ある程度使へるし ゲームなんかもできちゃったりする といふことかな・・・ それこそ私は 子供達が 最初に學ぶプログラミグ言語は Haskell がいいと思ってゐる
そのくらい この言語は 扱ひやすいし 直感的にも 理解しやすい部分が多い そして Haskellパラダイムに 最初に触れてゐた方が 變に命令型言語との對比などして頭を悩ませることもないし

ほんと Haskellを學ぶには 命令型言語の知識が必須みたいな風潮があるやうな氣がして 意味分かんない
プログラム初心者が Haskellを學ぶのは十分可能だし とても樂しい經驗になる筈だ