2025年9月30日火曜日

Haskell入門 モナドによるDSLの実現 ~operational~ P274

型の変換を細かく追って、整合性を確認はしてみましたが、けっこう込み入っていて、それに気を取られると全体の構造がわかりにくいので、おおまかにとらえることも大事かと思いました。runSalesTという関数が、再帰を使って、レシピを読み取っていく構造になっているようです。

 その際、ポイントが :>>=という関数のようです。 (ひとつの命令):>>= (それに続く残りの命令) この残りの命令である右辺がkとなっていて、 これが次のrunSalesTへ再帰で渡される構造になっているようです。そして、次のrunSalesTで、また先頭の(ひとつの命令)が取り出され、それに応じて枝分かれした処理が行われ、...これを繰り返していく。

  ちょうど、リストを順に処理していく再帰と同じ構造のようです。func( [a:k])=(何らかの処理   func(k)) の形。

 簡単なoperational使ったDSLのコードをChatGPTに頼んで作ってもらいました。

sample.hs

.yamlに- operationalを追加必要し stack runghc  sample.hsで7と表示します。
あるいはstack runghc --package operational sample.hsでもいいようです。

2025年9月29日月曜日

Haskell入門 Applicativeスタイル パーサー P251

 既習のモナドを組み合わせると、パーサーもこんなふうになるというよい例だと思います。ただ、かなり複雑にはなってきていますが。 20170401のような日付のパーサーの例です。

data YMD = YMD Int Int Int deriving Show    
countRead :: Read a => Int -> Parser Char -> Parser a
countRead i = fmap read . count  i  
ymdParser :: Parser  YMD                                                                    
ymdParser = YMD <$> countRead 4 digit <*> countRead 2 digit <*> countRead 2 digit 

1. YMD は関数:データコンストラクタ YMD は、単なる値ではなく
 YMD :: Int -> Int -> Int -> YMD という「Intを3つ受け取って YMD を返す関数」でもある。
2. countRead の役割:countRead n digit は「数字を n 個読み取り、それを整数として解釈する」パーサーである。
 例えば: countRead 4 digit :: Parser Int(年を読む)
      countRead 2 digit :: Parser Int(月や日を読む)
3. <$> で最初の適用:(<$>) :: (a -> b) -> Parser a -> Parser b
 a = Int  b = Int -> Int -> YMD  関数 f = YMD 値 Parser a = countRead 4 digit
 したがって:  YMD <$> countRead 4 digit :: Parser (Int -> Int -> YMD)
 ここで、Parser の中には「年をすでに受け取った部分適用関数 YMD y」が入っている。
4. <*> で次々に引数を渡す:次の <*> により
  Parser (Int -> Int -> YMD) <*> Parser Int :: Parser (Int -> YMD)
  さらにもう一度 <*> を使うと:
  Parser (Int -> YMD) <*> Parser Int ::  Parser YMD
※イメージ表せば
 <$>:外の関数を箱に入れる    <*>:箱の中の関数に箱の中の値を渡す 

2025年9月27日土曜日

Haskell入門 Readerモナド P186 

 testrun env = ( `runReader` env)  $ do ③
  cons1 <- consume
  cons2 <- consumue
  constOthers <-  local (\e->e{powSaveMode =True}) $ do
     cons3 <- consume              ①           ②
     cons4 <- consume
     return (cons3 + cons4}
  return (cons1+cons2+consOthers)

local :: (r->r) -> Reader r a -> Reader r a    
                 ①    ②          ①がモード切替の関数 ②do以降が対象

 「最初2回通常モード後半2回がSaveモードで電力を測定したものをすべて合計」というレシピをつくり(③のdo以降)それに、testrunのenvでその都度PowerEnvを入れてやる。そんな、流れでしょうか。

 (   ) `runReader` env   は runReader   (    )   env    ということ?のようです。(   )が未適用のところでここに③のdo以下のレシピがくるような、部分適用かと。

Haskell入門 P179 Alternative型クラスでMaybeを使う

let assocs = [("hiratara",39),("shu1",0),("masaharu",32)]
do age <- lookup "honma" assocs <|> lookup "hiratara" assocs   ①
      guard $ age < 20 ②
      return age 
②が、分かりづらかった。

guard True = pure ()  つまり Just ()
guard False= empty つまり Nothing

 「do式でMaybeを使った場合、①や②のどの行であっても、Nothingが出たらそこでストップして、Nothingを返す(つまり全体の結果がNothing)。最後まで行けばJust ageですが。」 
 この「どの行であっても」がポイントになりそうです。これがあるからモナドが便利なんだろうと思いますが。この性質は、以前にも目にしていて、そのときは理解していたつもりでしたが、しばらくすると忘れてしまうようで、きちんとした言葉でメモしておくことも大事かもしれません。

2025年9月26日金曜日

Haskell 代数的データ型、レコード記法(data)とnewTypeについて

 〇 data BookInfo = Book Int String [String] deriving (Show)
型コンストラクタ名 =データコンストラクタ名 フィールドの型1 フィールドの型2,,,
(新しい型の名前=型構成子)   (値構成子)      (構成要素1,2,,,,)
データコンストラクト名を関数として扱い、Int,String,,,という型の引数に適用するとBookInfoの型の新しい値を生成できる   myInfo=Book 978~ "〇〇" ....

〇 data Bool = False | True   代数的データ型  パターンマッチで使用

〇 レコード記法:フィールドに名前
data Employee = NewEmployee { employeeAge :: Integer , employeeName::String,...}
 employee = NewEmployee { employeeName="~ ,em~   }
   employeeName employeeで名前にアクセスできる アクセサは関数

〇newType NewtypeInt  = N Int deriving (Eq,Ord,Show) N 1< N 2は Ordで可、+不可
    data DataInt = D Int  deriving (Eq,Ord,Show)と比べると制限がある
 ・ちょうど一つのフィールドのみ可   newType Okay=ExactlyOne Int
 ・パラメータは問題なし  newType Param a b = Param (Either a b)
    レコード記法も可  ただしフィールド無は不可 複数の値構成子も不可

newType NTIndexed a =NewNTIndexed { unNTIndexed :: (Integer,a)} deriving Show
 NewNTIndexedとunNTIndexedが、NtIndexed aと (Integer,a)を行き来するための関数として使えるのが便利らしい。

※なお、newtype Ph a = MkPh (Int,a)として、:t MkPhとしてみたらMkPh :: (Int, a) -> Ph aとなりました。 外側から見ると Ph aでも、中身は (Int,a)ということになります。内部構造 (Int, a) は隠して、簡潔に見せたいという考え方?

Haskell 型クラスについて

 Haskell入門とRealWorlHaskellあたりを参考に型クラスについて まとめたみた
Java         Haskell
インターフェース   型クラス     型がもつべきメソッドを規定
具象クラス      インスタンス   規定されたメソッドの実装
インスタンス     値        個々のデータ

class BasicEq  a  where    ← BasicEqという型クラスを宣言
   isEqual ::  a -> a -> Bool          インスタンスの型はaで表現 
   isEqal x y = not (isNotEqual x y)    ←このようなディフォルト実装あれば、インスタンス
   isNotEqual :: a -> a -> Bool    で繰り返しの実装を省力化できる
   isNotEqual x y= not (isEqual x y)

 この型クラスのインスタンスである型は この型クラスの中で定義されている関数を実装しているならどんな型でもいい。 この型クラスでは1つだけ関数を定義している。

  関数の型を並べるときにその名前(a)をインスタンスのクラスを表すのに使うことが必要。
:t isEqualを確認すると 
  isEqual ::  ( BasicEq a ) => a->a-> Bool  
 これは、「任意の型aに対して aがBasicEqのインスタンスである限り、isEqual は 型aのパラメータを2つ取り、Boolを返す」 

isEaqualの定義は
instance BasicEq Bool where
  isEqual True True = True
 isEqual False False =True
 isEqual _  _  =False
instance BasicEq Color where
  isEqual Red Red = True
 .....  のようにインスタンスの種類を増やせる

2025年9月22日月曜日

Haskell入門 P390

 サーバー側のビルドがうまくいかず、苦労していたけれど、deriveJSONエラーが、どうやら、上にあるものですでに処理されているものを、下の方で使うということらしい。順序を次のように変えてみたら、うまくビルドできた。最近の仕様は厳しくなっているのかもしれない。

deriveJSON defaultOptions ''Money
deriveJSON defaultOptions ''AuctionItemId
deriveJSON defaultOptions ''ItemId
deriveJSON defaultOptions ''NewItem
deriveJSON defaultOptions ''Item
deriveJSON defaultOptions ''Term
deriveJSON defaultOptions ''NewUser
deriveJSON defaultOptions ''UserId
deriveJSON defaultOptions ''Inventory
deriveJSON defaultOptions ''AuctionRequest
deriveJSON defaultOptions ''AuctionServerRequest
deriveJSON defaultOptions ''AuctionException
-- deriveJSON defaultOptions ''UUID
deriveJSON defaultOptions ''User
deriveJSON defaultOptions ''AuctionItem
deriveJSON defaultOptions ''AuctionResponse
deriveJSON defaultOptions ''AuctionServerResponse

あとは、client.hsについては、以下行の削除したら、なんとかビルドが通った。
--import qualified System.Console.Haskeline.MonadException as Haskeline (catch)

--instance Catch.MonadThrow (InputT IO) where
--    throwM e = liftIO $ throwIO e
--instance Catch.MonadCatch (InputT IO) where
--    catch act handler = Haskeline.catch act $ \e ->
--        if isSyncException e
--            then handler e
--            else throwIO e

あとは、サーバ側のMain.hsがサンプルにはなかった?ようなので追加し、.cabalもそれに合わせてmain-isを修正すると、すべてビルドを終えることができた。

※サーバへのクライアントのアクセスも、GUIでなく、すべてコマンドライン操作なので、昔のパソコン通信のようなもので、久々に懐かしい感覚を思い出しました。

2025年9月18日木曜日

クラウド経由の家計簿データ保存

  これまで、raspiにテキストデータとして、外出中の買い物データを保存して、自作家計簿ソフトでインポートするようにしていたけれど、実際、旅行中など使ってみると、削除や編集の機能もないと不便だった。そこで、sqlite3を使って、raspiのbottleはWebAPI形式にしてみた。

 スマホはjavaでCRUDができるようにしたが、これが一番時間がかかった。完全にMVCに則ってコーディングするため、メンテはしやすいだろうけど、最初はなかなか大変。画面の該当行を長押しで、編集・削除もできるようにしたところ、けっこう便利になった。一般のクラウドでとなると、維持費もまあまあかかると思うので、個人利用なら、やはり、raspiが一番の節約になりそう。

 raspiは、省エネのわりに、SSRと組み合わせてタイマー機能、タッチパッドPC起動、ネットラジオ録音とさまざま活用できて手放せない存在になっている。

2025年9月6日土曜日

gitのリモートへのクローン(備忘録)

 githubにも、方法が表示されますが、念のため手順をまとめてみました。最近は、公開鍵も使うようになっているので。

・ mkdir myproject  cd myproject      git init
・ echo "# My Project" > README.md      git add .
  git commit -m "first commit"
・リモート側で空リポジトリ作成
   (例: GitHubで myproject を新規作成)

・ ssh-keygen -t ed25519 -C "your_email@example.com"(または -t rsa でもOK)>公開鍵 (~/.ssh/id_ed25519.pub) を GitHub の[Settings] → [SSH and GPG keys] → New SSH key に登録(アカウントのほうのSettingsで)>(~/.ssh/config)につぎのようにして443ポートを使うように指定、ルータのフィルタリングを通過させたい場合必要

Host github.com
  Hostname ssh.github.com
  Port 443
  User git

・ssh -T git@github.com で接続確認

・git remote set-url origin git@github.com:ユーザー名/リポジトリ名.git

   によりパスワード不要になる。

・ローカルのブランチ名が master の場合は git branch -M main で統一

・git push -u origin main

※毎回のプッシュを簡単にするためスクリプトを作ったら、けっこう便利。
git add .
git commit -m "commit_$1"
git push -u origin main
gitPush.sh に第一引数としてコメントも入れられるよう、上記のスクリプトにした


2025年9月4日木曜日

wslでtomcat10

root権限で ネット上の情報を参考にwslでtomcat10を動かしてみた。

wget https://downloads.apache.org/tomcat/tomcat-10/v10.1.44/bin/apache-tomcat-10.1.44.tar.gz
tar -xzvf apache-tomcat-10.1.44.tar.gz
apache-tomcat-10.1.44/bin/startup.sh で起動
apache-tomcat-10.1.44/bin/shutdown.sh で停止
localhost:8080でWindowsからも見ることができた。
pico apache-tomcat-10.1.44/conf/tomcat-users.xml
 <role rolename="manager-gui"/>
  <user username="admin" password="pass" roles="manager-gui"/>

myapp/  <---プロジェクト名
 ├─ WEB-INF/
 │    ├─ classes/
 │    └─ lib/
 └─ src/
           └─ HelloServlet.java という構成にして
つぎに、jspはwebappsの下のプロジェクト名直下において、表示なるか確認
index.jsp
http://localhost:8080/myapp

そして、servletは プロジェクトのディレクトリで
javac -d WEB-INF/classes  src/HelloServlet.javaで、コンパイルすると、表示が確認できる。
ライブラリ指定必要な時は、javac -d WEB-INF/classes -cp /home/user/tomcat10/apache-tomcat-10.1.44/lib/servlet-api.jar src/HelloServlet.javaのように-cpオプションで指定

HelloWorld.java
http://localhost:8080/myapp/hello

2025年9月1日月曜日

Haskellのテストの仕様について

注意点が2つほど  このへんは、ChatGPTでもなかなか気づけなかった。まだ、試行錯誤できる人間よりは、AIは不利かもしれない。

・テストファイルひとつならSpec.hs のままでも OK
しかし、複数だと、Main.hsとしないとだめな仕様になっているようです。このへんは、気づきにくいところ。

・cabalファイルにはpackage.yamlの設定が反映されるけれど、個別のファイルの設定は反映されず、globalスコープのdependencies:のところに、依存関係を記述しないとだめだった。

dependencies:
  - base >= 4.7 && < 5
  - text
  - bytestring
  - aeson
  - sqlite-simple  などのように