2025年10月5日日曜日

Haskell入門 型以外の安全性の確保 P286

 actionIO :: IO a -> IO a
actionIO action = do
  mv <- newEmptyMVar    --スレッド間通信用の空のMVarの箱をつくる
  _tid <- forkIO $ do           --スレッドをたてて
    result <- try action         --actionを実行 成功ならRight 値  例外なら Left eを返す
    putMVar mv result         --結果(Either SomeException a)をMVarに入れる
  result <- takeMVar mv    --メインスレッドでMVarから結果を受ける。入るまで待つ動作。
  case result of                       つまり、actionが終わるまで待って結果を受け取る
    Left e  -> throwIO e
    Right r -> return r

 forkIOでスレッドをわけると、普通はその中で起きた例外が親スレッドに伝わらない。
でも、この関数では、例外をつかまえ MVarを通して親スレッドに返している。

久しぶりに室内MLAアンテナでDX

 18MHzバンドで久しぶりDXが開けていた。ゼヤ(Asiatic Russia)の局長さんとFT8で交信できた。5Wの室内アンテナで届いたので、コンディションがよかったようだ。

 FT8でオンエアしている局の密度をみると、やはり、欧州、日本、北米が目立っているのがわかる。

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) は隠して、簡潔に見せたいという考え方?