WinHugs
アケオメ、コトヨロ
unix系は色々なhaskell系が有るんで、好きに入れればいいけど、Windows系はどうする? ghcをいれようとすると、色々なものを引き連れてきて、大掛かり過ぎる。
ならば、WinHugsだな。 まずは最小の方でもと思って取って来てインストールしようとしたら、途中でコケて インストール出来ない。Hugs98.chmが、Could Not ... とか言って展開しないんだ。 結果、インストール失敗と相成る。 もう一つの大きい方も同様にコケる。古いソフトですからコケが 生えたんですな。
違うって、オイラーがいつまでたってもWindows7からWindows10に アップデートしないものだから、M$が嫌がらせしてるんですよ。早く10にしろ10にしろと 起動のたびに出てきて、M$ウィルスに感染しましたな。chmなんていう恥ずかしい遺産を 早く亡きものにしたいのです。
どうしてくれよう? exeファイルって、zipファイルに自己解凍のunzipを合体させた ものだろうと野生の感が働いた。zipを展開するソフトにかけたら、中身が出てきた。 dir毎、適当な所に配置して、winhugsのアプリのリンクを作って、設置完了。
正規のインストールじゃなくて、野良インストールなんで、ファイルの関連付けなんて勿論出来て いない。試運転を兼ねて、life.lhsを起動しましょ。そうすると、このファイルは、どの アプリから起動しますかって聞いてくるんで、Winhugsを指定。通常よく使われてhoge.hs とかもWinHugsに関連付けておこう。
試運転は、life.lhsだな。このアプリを解説してる『プログラミングHaskell』(オーム社)本は、 非常に良い本、何度でも読み返したくなるSICPみたいな本だぞ。
走らせてみると、グライダーが滑降しないで、
[1;320HO[2;320HO[3;220HO[2;120HO[3;320HO{Interrupted!} (39823879 reductions, 51863040 cells, 57 garbage collections) Main>
こんなのが出てきたんで、止めるボタンを押して止めた。WinHugsのコンソール画面って エスケープシーケンスが効かない、Windowsな世界なのね。
悪あがきのついでに、コマンド.コムな端末窓からhugsを起動してlife.lhsが動くか確認。 やっぱりエスケープシーケンスを理解出来なくて、そのまま文字表示しちゃう。
端末窓は他に無いのか?ってゲイツ閣下に聞いてみたら、DOS由来の窓から少し進化した 体系が有ると言う。もったいぶっていないで早く教えろ。
それはね、Power Shellの窓でございます。今お使いのWindows7 Home-エディションにも 搭載してございますから、アクセサリーの所を見てください。
シェルと言う割りには起動が遅いな。で、試してみるとやはりエスケープシーケンスは 理解出来ない窓端末でした。そのわりには、emacs -nw で、窓にemacsを貼り付けると ちゃんと動くなあ。emacs君は大分無理して、Windowsに合わせているんでしょう。 ご同情申し上げます。
頑張ってWindowsでもコマンドしたい!ConEmuとMSYS2を入れてみよう をやるか。cmd.exe基礎 にも紹介が有るなあ。
試しにConEmuを入れてみた。ちゃんとエスケープシーケンスを理解し、グライダーが滑降 してくれた。こうでなくちゃね。
ああ、ConEmuの設定とか扱い方は、 Windows標準のコマンドプロンプトウィンドウをタブ化できる「ConEmu」 に出てた。WindowsでUnixもどきを体験したい時は必須のソフトだな。
最近はmsysもmsys2に進化して、pacmanを使ってるんか。MSYS2 installer
Win32
でWinの流儀でGUIかと気付いたオイラーは、Windowsユーザーだけの特典があるに違いないと 思って、提供物を家宅捜査してみた。demos\Win32\hello.lhs を発見、補足しました。
こいつを読みやすくする為に、早速emacsにhaskell-modeを突っ込んであげましたよ。そして、
; hugs (setq haskell-program-name "hugs") (add-hook 'haskell-mode-hook 'turn-on-haskell-indent) (autoload 'haskell-mode "haskell-mode") (add-to-list 'auto-mode-alist '("\\.hs$" . haskell-mode)) (add-hook 'haskell-mode-hook 'inf-haskell-mode)
こんなemacsの設定を書いたら、haskell-modeからhugsを呼び出せたよ。
ああ、WinHugsを起動した時にロードされるhsなファイルを表示するように設定してる。 それがリンクになってるんで、クリックするとemacsが立ち上がって閲覧出来る。 さすがGUIの世界だな。
module Main(main) where import qualified Graphics.Win32 import qualified System.Win32.DLL import qualified System.Win32.Types import Control.Exception (bracket) import Foreign import System.Exit main :: IO () main = Graphics.Win32.allocaPAINTSTRUCT $ \ lpps -> do hwnd <- createWindow 200 200 (wndProc lpps onPaint) messagePump hwnd onPaint :: Graphics.Win32.RECT -> Graphics.Win32.HDC -> IO () onPaint (_,_,w,h) hdc = do Graphics.Win32.setBkMode hdc Graphics.Win32.tRANSPARENT Graphics.Win32.setTextColor hdc (Graphics.Win32.rgb 255 255 0) let y | h==10 = 0 | otherwise = ((h-10) `div` 2) x | w==50 = 0 | otherwise = (w-50) `div` 2 Graphics.Win32.textOut hdc x y "Hello, world" return () {- 以下、ぐちゃぐちゃとコードが続く -}
ってなのが載ってて、走らせてみると、ハロワ画面が出てきましたよ。
HGL
もっと簡単なの、お隣にあった。HGLとかいうハロワ。
module Main(main) where import Graphics.HGL main :: IO () main = runGraphics $ do w <- openWindow "Hello World Window" (300, 300) drawInWindow w (text (100, 100) "Hello") drawInWindow w (text (100, 200) "World") getKey w closeWindow w
これだけで、GUIな窓が出てくる。何かキー入力すると窓が引っ込む。なんかTkっぽいな。 実用にするには、裏で色々ロードしてるWin32のモジュールとかHGLのモジュールを 見ておかないといけないね。Test.hsとかGTest.hsは多少参考になるけど、他に 何処かに資料を置いていないかな?
ぐぐってみたら、自分が昔やったのが出てきた。 もう6年も前。時代は巡るなあ。
走らせてみると
ERROR file:.\ball.hs:54 - Type error in value construction *** Expression : BallObj {dx = speed0 * 4, dy = speed0, x = 0, y = 0} *** Term : speed0 * 4 *** Type : Double *** Does not match : Float
こんな風に文句を言ってきたんで、Doubleに全部変更。そしたら、あっさり動いた。 昔は、Windowsにghcの組み合わせで悪戦苦闘してたってのに、どゆ事。hugsの方が出来が いいじゃん。
ghc系でも動くはずってんで、HGL package になってるのはいいんだけど、使い方の説明って無いの? 自慢の
Main> :m Graphics.HGL Graphics.HGL> :browse module Graphics.HGL where runGraphics :: IO () -> IO () Unbuffered :: RedrawMode -- data constructor : par :: IO a -> IO b -> IO (a,b) par_ :: IO a -> IO b -> IO () parMany :: [IO ()] -> IO ()
これだけで、コードを書けってのは、大いに無理が有りますよ。 The hgl-example packageでも 読んでくださいって事かな。
いずれにしてもGUIは労多くして得る所無しって代物だから、余り近寄らない方がいいぞ。
hugs basic
去年書いた、まともなhaskellアプリがgoferで動かないか検討中。何ってたって、hugsはgoferを 元に発展させませたって堂々とWinHugsのFAQに書いてありましたから。。。
現代のものが、昔の流儀で動くかプチ興味が有ったのさ。で、preludeを眼grepした限りでは、 文字列な数字を整数に変換する関数が見つからなかった。そのあたりを補う方法を 考えておこう。
C語で言うと、atoiって関数。どうやって実現してる?ちょいとOpenBSDにスパイを放って みる。(NetBSDのそれは、ちょっとぐちゃぐちゃしてて、追うのは大変)
/* /usr/src/lib/libc/stdlib/atoi.c */ int atoi(const char *str) { return((int)strtol(str, (char **)NULL, 10)); }
/* /usr/src/lib/libc/stdlib/atrtol.c */ long strtol(const char *nptr, char **endptr, int base) { : for (acc = 0, any = 0;; c = (unsigned char) *s++) { if (isdigit(c)) c -= '0'; : acc *= base; acc += c; : return (acc); }
ふむ、atoiってのはフロント役をやってて、バックにはstrtolとかstrtollが控えて いるのね。で、バックの仕事ぶりは、結構精緻になってた。本質が欲しいので、しっぽから 見てくと、accが答えとな。遡ってみると、acc = acc * 10 + c ってのが骨格だ。 計算結果を累積してんのね。
方法が分かれば、後はそれをHaskell流に移植するだけ。
-- Like atoi strtol import Data.Char d2i :: Char -> Int d2i c = ord c - ord '0' ri :: String -> Int ri s = ri' s 0 ri' "" _ = 0 ri' (s:"") n = n * 10 + d2i s ri' (s:ss) n = ri' ss (n * 10 + d2i s)
hugsでやる時は、ordがData.Charに移ってしまったので、importしておかないと悲しい事に なる。riがatoiに相当して、riダッシュってのがstrtolに相当するかな。d2iは一桁の文字を 数値に変換するやつだ。空文字の場合は未定義でもよかったんだけど、慣習でゼロを 返している。
そんじゃ、hugsでは、どう実現してるか、見ておく。Numericモジュールの素性を調査。 hugsの場合、コロンmコマンドは、現モジュールを切り替える機能しかないので、あらかじめ ロードしておく事。ghciは、コロンmコマンドで、モジュールを取り込んでくれる親切設計に なってた。
after import Numeric :m Numeric Numeric> :browse module Numeric where showSigned :: Real a => (a -> ShowS) -> Int -> a -> ShowS showIntAtBase :: Integral a => a -> (Int -> Char) -> a -> ShowS showInt :: Integral a => a -> ShowS showHex :: Integral a => a -> ShowS showOct :: Integral a => a -> ShowS showEFloat :: RealFloat a => Maybe Int -> a -> ShowS showFFloat :: RealFloat a => Maybe Int -> a -> ShowS showGFloat :: RealFloat a => Maybe Int -> a -> ShowS showFloat :: RealFloat a => a -> ShowS floatToDigits :: RealFloat a => Integer -> a -> ([Int],Int) readSigned :: Real a => ReadS a -> ReadS a readInt :: Integral a => a -> (Char -> Bool) -> (Char -> Int) -> ReadS a readDec :: Integral a => ReadS a readOct :: Integral a => ReadS a readHex :: Integral a => ReadS a readFloat :: RealFrac a => ReadS a lexDigits :: ReadS String fromRat :: RealFloat a => Rational -> a Numeric> readHex "fffx3" [(4095,"x3")]
最後に、ちょろっと、16進を数値に直してみた。失敗した所と成功した所と分けて 返ってくる仕様なのね。
readDec = readInt 10 isDigit (\ d -> fromEnum d - fromEnum_0) readInt :: Integral a => a -> (Char -> Bool) -> (Char -> Int) -> ReadS a readInt radix isDig digToInt s = [(foldl1 (\n d -> n * radix + d) (map (fromIntegral . digToInt) ds), r) | (ds,r) <- nonnull isDig s ]
10進変換の定義。複雑な内包処理をしてるなあ。どういう動きをするのか追ってみる。 内包表記なんで、縦棒の左と右を考える。まずは右から。nonnullって何だ?
type ReadS a = String -> [(a,String)] nonnull :: (Char -> Bool) -> ReadS String nonnull p s = [(cs,t) | (cs@(_:_),t) <- [span p s]]
spanして出来るタプルをリストに押し込めたものかな。
Main> span isDigit "123x456" ("123","x456") Main> nonnull isDigit "123x456" [("123","x456")]
次は縦棒の左側。棒の右側の結果の正常な文字列を整数に変換し、異常文字を含むものと タプルに組み上げているんだな。
foldl1 (\n d -> n * radix + d) (map (fromIntegral . digToInt) ds)
mapを使って、数字文字列を、Intなリストに変換。
Main> map (fromIntegral . digToInt) "12345" [1,2,3,4,5]
ここに出て来るdigToIntは、オイラーの定義した d2iの事ね。d2iで使ってるordってのは 文字コードに相当する整数を返す関数。昔はASCIIコードだけを対象にしてれば良かったけど、 世の中の動きにつれて、全世界共通文字コードを返すように拡張されてる。だからきっと、 漢数字でも動くに違いない。壱弐参。
Main> ord '壱' ERROR - Improperly terminated character constant
Hugsってまだサポートが足りない? ghcだと
Prelude Data.Char> ord '壱' 22769 Prelude Data.Char> ord '弐' 24336
ああ、無駄な事をしたわい。話を戻して、得られた 整数リストに対してfoldl1を適用するんか。 foldl1ってのは
foldl1 :: (a -> a -> a) -> [a] -> a foldl1 f (x:xs) = foldl f x xs foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs
リストの最左の値を初期値として、左畳み込みをするんだな。畳み込みは右からも出来る。 更に言語による流派もある。詳しくは、リストの畳み込みを参照
etc
去年のAdvent Calendarで興味を引いたのは、NetBSDの所にあったもの。
そしてHaskellグループでも。
SMLも頑張っている。 SML#でJITコンパイラを作る軽い話