new ghc
Table of Contents
new ghc
ghcのソースを見つけてしまったものだから、試してみたい。
https://www.haskell.org/ghc/ ああ、これがGHC専用サイトだな。
haskellと言えばGHC、野球と言えば大谷のごとく、裏で得体の知れない大きな 力が作用してると思うんだけど、どうよ?
9.2.x, 9.4.x, 9.6.x の3本柱体制なのか。ならば先端を行ってみたい。多分、 茨の道だろうけど。
INSTALL.md
Quick start: the following gives you a default build: $ ./boot $ ./configure $ ./hadrian/build
こんな工程が案内されてた。bootのフェーズで、必須のモジュールのソースが DLされて、既存のghcにより、configureが作成されるんだな。
OpenBSD(32bit)では、種になるghcが既に無いので、新しいghcは作成出来ない 状態になっている。
Configure completed successfully. Building GHC version : 9.7.20230601 Happy : /home/sakae/.cabal/bin/happy (1.20.1.1) -- yacc Alex : /home/sakae/.cabal/bin/alex (3.3.0.0) -- lex or flex sphinx-build : xelatex : makeinfo : /usr/bin/makeinfo git : /usr/bin/git cabal-install : /home/sakae/.ghcup/bin/cabal ---------------------------------------------------------------------- For a standard build of GHC (fully optimised with profiling), type (g)make. To make changes to the default build configuration, copy the file mk/build.mk.sample to mk/build.mk, and edit the settings in there. For more information on how to configure your GHC build, see https://gitlab.haskell.org/ghc/ghc/wikis/building
configure時に、happyとalexは必須だ。追加で入れたよ。ドキュメント関係に はpythonも必要っぽいけど、とりあえず省略。そして、最後の長い工程へと進 んで行く。
| Run Ghc CompileHs Stage1: compiler/GHC/StgToCmm/Prim.hs => _build/stage1/compiler/build/GHC/StgToCmm/Prim.p_o ^C real 219m5.335s 35m43.883s user 200m18.025s + 31m22.239s sys 15m40.545s 1m37.070s
とんでもない時間がかかったので、途中一回休み。翌日、同じコマンドを叩い たら、継続してくれた。stage1が有るって事は、stage2も、やり方によっては 作成できるんだな。
-j8ぐらいで使えるマシンが欲しい。Windows7時代のi386な 石じゃ、何をするにも役不足。
/----------------------------------------------------------\ | Successfully built program 'ghc-bin' (Stage1). | | Executable: _build/stage1/bin/ghc | | Program synopsis: The Glorious Glasgow Haskell Compiler. | \----------------------------------------------------------/ sakae@deb:~/MINE/src/ghc$ cd _build/stage1/ sakae@deb:~/MINE/src/ghc/_build/stage1$ ls bin/ ghc/ lib/ libraries/ share/ compiler/ inplace/ libffi/ rts/ utils/ sakae@deb:~/MINE/src/ghc/_build/stage1$ ls -sk bin/ total 10416 2172 ghc* 5944 haddock* 712 hpc* 64 runghc* 612 ghc-pkg* 52 hp2ps* 860 hsc2hs* sakae@deb:~/MINE/src/ghc/_build/stage1$ bin/ghc --version The Glorious Glasgow Haskell Compilation System, version 9.7.20230601
軽く確認。
sakae@deb:~/MINE/src/ghc/_build/stage1$ bin/ghc-pkg list /home/sakae/MINE/src/ghc/_build/stage1/lib/package.conf.d Cabal-3.10.1.0 Cabal-syntax-3.10.1.0 array-0.5.5.0 base-4.18.0.0 : ghci-9.7 :
全体が4.8Gと言う巨大なものになった。モジュールは同梱してくれているけど、 肝心のスタンダローンで動くcabalが無い。どうやって作成するんだろう? これが無いと、何も出来ないな。juliaみたいに、パッケージャーと一体になっ た物が便利と思うぞ。
次は、 hadrian のあたりを見て突き進めばいいのか。なんか、古いパソコンに負担をかけるの は、気が引けるなあ。
Linux vs. *BSD
ほっとして翌日Linuxを起動したら、WiFiが死んでいた。じゃ、FreeBSDではど うかと試したら、やはりダメ。shutdown -r nowして、grubでFreeBSDに切り替 えちゃったんで、ハードのリセットが効かなかったかも。 きっと、前日の疲れが残っていたんでしょうと言うことで、諦め。
翌日リナを起動すると、やはりWiFiが死んでた。しょうがない、本腰を入れて 調べてみるか。dmesgだな。が、不親切で何も記録はなし。次は/var/log あたりか。面倒なので、FreeBSDに切り替え。dmsgしたら、wlan0なデバイスが disableになってるよと一言あった。
ああ、思い出した。ハードのSWがついてた事を。点検したらOFFになってた。 夜中にダエモン君が出てきて、余りパソコンを酷使しないで下さいと、警告し てったんだな。
それにしてもリナのdmsgにはハードに対する愛が無いと思うぞ。対して*BSDな 人は、ハードを愛してる。余りに愛すぎるばかりに、VAXなミニコンのボード をひっこぬき、パターン・カット、ICの足あげとかジャンパー線を飛ばす改造 も訳なくやっちゃう。こういう人の元で成長してきたBSDは、必要にして十分 な情報が提供されてるよ。
3種の神器、半田コテ、パターンカット用の電動グラインダー、スェーデン製 の刃先がモリブデン鋼で出来たニッパー。ええ、オイラーも現役時代に使って いましたとも。
debug
思い出したように、ghciのdebuggerで遊ぶ。
ghci> :break Eval.textToEvalForm Breakpoint 0 activated at app/Eval.hs:107:28-93 ghci> :main -r Repl> (+ 3 4) Stopped in Eval.textToEvalForm, app/Eval.hs:107:28-93 _result :: LispVal.Eval LispVal.LispVal = _ input :: Data.Text.Internal.Text = _ std :: Data.Text.Internal.Text = _ [app/Eval.hs:107:28-93] ghci> :set stop :list
stepで止まった時にリストする様に指示
[app/Eval.hs:107:28-93] ghci> :step Stopped in Eval.textToEvalForm, app/Eval.hs:107:28-68 _result :: Either Text.Parsec.Error.ParseError LispVal.LispVal -> LispVal.Eval LispVal.LispVal = _ 106 textToEvalForm :: T.Text -> T.Text -> Eval LispVal 107 textToEvalForm std input = either (throw . PError . show ) evalBody $ parseWithLib std input ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 108
ちょっと鬱陶しいけど、何処を評価するか表示してくれるようになった。
[app/Eval.hs:96:13-35] ghci> show std "\"(define caar (lambda (pair) .... (even (- x 1)))))\\n\\n\\n\""
そして、何でも見せますが便利だ。
Haskellでのデバッグ手法 こういう楽しいのが有るなあ。
第15回 Haskellでのデバッグのコツをつかむ 大事な話だ!!
Haskell でのデバッグ 山本先生降臨
Debug.trace
お勧めとの事なので、やってみる。ソースを変更して実行するので、ghc –make の、PDCAが速く回せる環境が良い。
指定した場所を通過したか、変数の値はどうか、確認出来る。
Eval.hs
import Debug.Trace textToEvalForm :: T.Text -> T.Text -> Eval LispVal textToEvalForm std input = either (throw . PError . show ) evalBody $ parseWithLib std $ trace ("in Form" ++ show std) input
元は、 parseWithLib std input となっていた場所に割り込み。通過した印に文字列と引 数を表示。あとは何事も無かったように振る舞う。$ を忘れるな。
sakae@deb:/tmp/cs/app$ ./a.out -r Repl> (+ 3 4) inForm "(define caar (lambda (pair) ... #f (even (- x 1)))))\n\n\n" 7
確かに、通過しましたねぇってのが判る。割り込む場所の選定に、頭を悩ます な。
Exception
使っているんだよなあ。ちゃんとよんでおく。
実際は、throwが、下記の2例の方法で利用されている。
parseFn val = throw $ TypeMismatch "parse expects string, instead got: " val lineToEvalForm input = either (throw . PError . show ) eval $ readExpr input
投げる例外は型が定義されてる。下記が例の一部。
data LispException = NumArgs Integer [LispVal] | TypeMismatch T.Text LispVal | PError String -- from show anyway : instance Exception LispException instance Show LispException where show = T.unpack . showError
例外の実例はリスプの例外ですよ、リスプ例外の見せ方は、こうだからねって 宣言。そして、そのエラーの具体的な表示って訳だ
showError :: LispException -> T.Text showError err = case err of (TypeMismatch txt val) -> T.concat ["Error Type Mismatch: ", txt, showVal val] (PError str) -> T.concat ["Parser Error, expression cannot evaluate: ",T.pack str] :
repl
schemeとかはreplが使用される。そんなのは既製品として用意されてる。 importを眺めて、どんなのが利用されてるか知っておくのが先決。 Repl.hsと言うもろな名前にある。
import System.Console.Haskeline ( defaultSettings, getInputLine, outputStrLn, runInputT, InputT )
これを手がかりに、hoogleしたら
import System.Console.Haskeline main :: IO () main = runInputT defaultSettings loop where loop :: InputT IO () loop = do minput <- getInputLine "% " case minput of Nothing -> return () Just "quit" -> return () Just input -> do outputStrLn $ "Input was: " ++ input loop
こんな例が出てきた。実際は、こんなコードが使われていた。
mainLoop :: IO () mainLoop = runInputT defaultSettings repl repl :: Repl () repl = do minput <- getInputLine "Repl> " case minput of Nothing -> outputStrLn "Goodbye." Just input -> liftIO (process input) >> repl process :: String -> IO () process str = do res <- safeExec $ evalText $ T.pack str either putStrLn return res
replのループが、さりげなく >> に書き換えられている。
こうなっていると、昔を思い出すな。集積回路の仕様書を眺めると、スペック と共に、使用例が掲載されている。その例を基に実際の回路を設計する。ハー ドもソフトも一緒だな。
ついでに、safeExecなんてのを調べておくか。何となく保護回路っぽい匂いが するぞ。
safeExec :: IO a -> IO (Either String a) safeExec m = do result <- Control.Exception.try m case result of Left (eTop :: SomeException) -> case fromException eTop of Just (enclosed :: LispException) -> return $ Left (show enclosed) Nothing -> return $ Left (show eTop) Right val -> return $ Right val
やっぱり、監視機能付きで実行してみて、異常が有ったら、即座に例外をあげ るってなってる。ハードの場合は、異常が有ると、匂いがしたり、煙が出たり、 爆発したりしますからねぇ。その点、ソフトはソフトですね。甘っちょろいと も言えるな。何度もやり直しOKですから。
use parts on ghci
今度は、保護装置無しで、直接ghciから動かしてみる。完成した装置を使って いたら、効き具合が検証出来ないからね。
ghci> import Data.Text as T ( pack ) ghci> import Eval ( safeExec, evalText ) ghci> evalText $ T.pack "(+ 3 4" *** Exception: Parser Error, expression cannot evaluate: "<stdin>" (line 1, column 7): unexpected end of input expecting "#", nil, "-", "+", digit, identifier, literal string, "'", "(" or ")"
必要な部品をimportする。そして、わざとエラーになる入力を与えた。見事に 例外が発生。
ghci> safeExec $ evalText $ T.pack "(+ 3 4" Left "Parser Error, expression cannot evaluate: \"<stdin>\" (line 1, column 7):\ nunexpected end of input\nexpecting \"#\", nil, \"-\", \"+\", digit, identifier, literal string, \"'\", \"(\" or \")\""
今度は、保護機構付きで実行。ちゃんとLeftだよと、言ってきた。無様に例外 が発生するのを回避してる。
opt
コマンドラインの解析は、どうやってるか? CLIなアプリを作る場合は切実だ。 見るべきファイルは、もろにズバリのCli.hs。大半を占めているのは、 Options.Applicative と言う部品。いや、部品と言うとオイラーの場合は、 2SC372とかのトランジスタが頭に浮ぶ。この場合は、もっと機能豊富だろう。 少くとも4004ぐらいな、世界初のワンチップCPUぐらいを想像せい。使った事 がない、石は知りません。
パーサーなんで、初心者には、扱いがムズイ。アプリケーションノートが公開 されている。
optparse-applicative Quick Start
こういうのが無くちゃ、採用して貰えませんよ。十分、眩暈がするけどね。
scheme 2のパーサーは、どうなっている?
readExpr :: T.Text -> Either ParseError LispVal readExpr = parse (contents lispVal) "<stdin>"
成功すればS式の内部表現が返り、失敗すればパースエラーって風になってる。
ghci> import Parser ( readExpr, readExprFile ) ghci> :t readExpr "(+ 3 4)" readExpr "(+ 3 4)" :: Either Text.Parsec.Error.ParseError LispVal ghci> readExpr "(+ 3 4)" Right (+ 3 4)
内部表現の確認。
ghci> import LispVal ( LispVal(List, Bool, Nil, Number, String, Atom) ) ghci> :t List [Number 3, String "abc", Atom "fact", Nil, Bool True] List [Number 3, String "abc", Atom "fact", Nil, Bool True] :: LispVal ghci> List [Number 3, String "abc", Atom "fact", Nil, Bool True] (3 "abc" fact '() #t)
hashVal :: Parser LispVal hashVal = lexeme $ char '#' *> (char 't' $> Bool True <|> char 'f' $> Bool False <|> char 'b' *> (Number <$> intRadix (2, oneOf "01")) <|> char 'o' *> (Number <$> intRadix (8, octDigit)) <|> char 'd' *> (Number <$> intRadix (10, digit)) <|> char 'x' *> (Number <$> intRadix (16, hexDigit)) <|> oneOf "ei" *> fail "Unsupported: exactness" <|> char '(' *> fail "Unsupported: vector" <|> char '\\' *> fail "Unsupported: char")
schemeのリーダーの領分。#に続いてxが見付かると、それは16進数です。とか、 e表現は、浮動小数点数、iは複素数の表現成分なんで、まだサポートしてませんです、なんてのが表 現されている。
Repl> (+ #x10 #o1) 17 Repl> (+ #x10 #o9) Parser Error, expression cannot evaluate: "<stdin>" (line 1, column 11): unexpected "9" expecting "-", "+" or octal digit
オクタルな数字に9は、有りません。ちゃんとチェックしてるね。