Write You A Scheme, Version 2 (2)
Table of Contents
try again
前回失敗してた、ArchLinux上での、schemeに実装されたWebアクセス。やはり、 cs.cabalを設定した時のミスであった。下記を追加。
ghc-options: -threaded -rtsopts -with-rtsopts=-N
普通に起動すると、エラーになる。積極的にメモリーを指定すると、やっと起 動した。
[sakae@arch cs]$ cabal exec -- cs -r cs: mmap 4096 bytes at (nil): Cannot allocate memory cs: Try specifying an address with +RTS -xm<addr> -RTS Segmentation fault (core dumped) [sakae@arch cs]$ cabal exec -- cs +RTS -xm20000000 -RTS -r Repl> (wslurp "http://www.yahoo.co.jp") " " Repl> (wslurp "http://localhost:8080/") "<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="utf-8"> <title>Directory listing for /</title> :
普通の所は、httpでアクセスしてもhttpsにリダイレクトされてしまい、結果 は無しになった。pythonで昔ながらのサーバーを建てて、そこにアクセスする と、やっと結果が得られた。
top から、抜粋
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND : 268 sakae 20 0 8940 4608 3840 S 0.0 0.3 0:00.06 bash 272 sakae 20 0 90216 32756 8332 S 0.0 2.2 0:01.26 emacs 469 sakae 20 0 1024.0g 13696 5504 S 0.0 0.9 0:00.12 cabal 474 sakae 20 0 1024.4g 24428 7424 S 0.0 1.6 0:00.33 cs
ps awx から、抜粋
469 pts/2 Sl+ 0:00 cabal exec -- cs +RTS -xm20000000 -RTS -r 474 pts/2 Sl+ 0:00 /tmp/cs/dist-newstyle/build/x86_64-linux/ghc-9.2.7/cs-0.1.0.0/x/cs/build/cs/cs +RTS -xm20000000 -RTS -r
on FreeBSD(32bit)
[sakae@fb /tmp/cs]$ cabal exec -- cs +RTS -xm20000000 -RTS -r cs: unknown RTS option: -xm20000000 cs: cs: Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args> cs: cs: +RTS Indicates run time system options follow cs: -RTS Indicates program arguments follow cs: --RTS Indicates that ALL subsequent arguments will be given to the cs: program (including any of these RTS flags) cs: cs: The following run time system options are available: cs: cs: -? Prints this message and exits; the program is not executed cs: --info Print information about the RTS used by this program :
エラーが先導してくれる、拡張知識だなあ。何が出てくるか玉手箱ですけど。
[sakae@fb /tmp/cs]$ cabal exec -- cs +RTS --info -RTS -r [("GHC RTS", "YES") ,("GHC version", "9.2.7") ,("RTS way", "rts_thr") ,("Build platform", "i386-portbld-freebsd") ,("Build architecture", "i386") ,("Build OS", "freebsd") : ,("Flag -with-rtsopts", "-N") ]
-xm20000000 こんな非人間的な記述しか駄目かと思っていたら、8k,8M,8G の ごとく記述が出来ると、そこはかとなく例示されてた。当然の事ですよ。 工学では、仮数のぶれより、指数を間違える方が恥しいです。
cabal build
[sakae@arch cs]$ cabal build -v >LOG [sakae@arch cs]$ wc LOG 359 1692 42953 LOG [sakae@arch cs]$ cat LOG | cut -b-78 >SHORT.log
無茶苦茶大変な事と言うか、前準備が丁寧。モジュールが上手く噛み合うよう に調整してる。これがcabalを使うご利益か。それから、引数とかの指定が丁 寧すぎるぞ。矛盾をきたさない様に配慮してんだな。
このログを見ていたら、No jhc found なんてのが有った。確かGHCのライバル だったんではなかろうか? hoogleで探してみたら、 CompilerFlavor に登録されてた。みんな何処へ消えた。GHCの一人勝ちって、 それでいいのか! 生物多様性ならぬhaskell多様性を望むぞ。
かの昔、ユカタン半島に落ちた天からの火の玉によって、恐竜は滅びた。現代 なら、邪悪なAIが闊歩して、 haskellはAIの世界征服の邪魔になる。気付かれないようにして、性能劣化さ せてしまえって画策したらどうなる。心配してる人達がいるぞ。 「AIによる人類絶滅のリスク」に警鐘。OpenAIやMicrosoft役員ら多数が署名
only GHC build
上記の反省にたって、もっとカジュアルにbuildしてみたい。ghcの解説書によ れば、ghc –make Main.hsで、よきに計らってくれるはずなんだけど、やはり それなりの穴が有ったよ。
[sakae@arch app]$ cat gen.sh #! /bin/sh ghc --make \ -o a.out \ -XOverloadedStrings \ -package parsec \ -package HTTP \ -package haskeline \ -package optparse-applicative \ -package mtl \ -threaded -rtsopts -with-rtsopts=-N \ Main.hs
-XOverloadedStrings は、それぞれのファイルに相当のプラグマが書いてあ れば不要なんだけど、さぼっていたのが有ったので追加。後はパッケージの指 定ね。-o a.outはオイラーの趣味。これを指定しないと、Mainが出来あがる。
cs.cabalのbuild-dependsと比べてみると、text,containers,directory,base は、指定せずともよかった(但し、FreeBSDでは必要でした)。GHCが用意した物で賄えたって事なんだな。
[sakae@arch app]$ sh gen.sh Loaded package environment from /home/sakae/.ghc/x86_64-linux-9.2.7/environments/default [1 of 7] Compiling LispVal ( LispVal.hs, LispVal.o ) [2 of 7] Compiling Parser ( Parser.hs, Parser.o ) [3 of 7] Compiling Prim ( Prim.hs, Prim.o ) [4 of 7] Compiling Eval ( Eval.hs, Eval.o ) [5 of 7] Compiling Repl ( Repl.hs, Repl.o ) [6 of 7] Compiling Cli ( Cli.hs, Cli.o ) [7 of 7] Compiling Main ( Main.hs, Main.o ) Linking a.out ...
ghcに -v とかを付けると、詳細が暴露されて面白いぞ。
もぐら叩き
うえのコンパイル用が、いとも簡単に出きた訳ではない。以下のようにして、 解決していった。
-XOverloadedStrings が無い場合
[1 of 7] Compiling LispVal ( LispVal.hs, LispVal.o ) LispVal.hs:54:35: error: • Couldn't match type ‘[Char]’ with ‘T.Text’ Expected: T.Text Actual: String
こんなのが永遠と出てきて萎える。普遍性のありそうな、couldn't match .. で、ググれば、ヒントが得られる。大量のエラーは一瞬にして消えるから、 安心したまえ。
haskelineが指定無しの場合
Repl.hs:12:1: error: Could not load module ‘System.Console.Haskeline’ It is a member of the hidden package ‘haskeline-0.8.2’. You can run ‘:set -package haskeline’ to expose it.
-package haskeline を追加。 必要な物が次々に出てくるので、忍耐強くもぐら叩きすれば、そのうちに、い なくなる。
with gdb
少し探検
sakae@deb:/tmp/cs/app$ gdb -q a.out Reading symbols from a.out... (gdb) r -r Starting program: /tmp/cs/app/a.out -r [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". [New Thread 0xb773fb40 (LWP 3187)] [New Thread 0xb68ffb40 (LWP 3188)] [New Thread 0xb5effb40 (LWP 3189)] [New Thread 0xb54ffb40 (LWP 3190)]
スレッド族
Thread 1 "a.out" received signal SIGINT, Interrupt. 0xb7fd2559 in __kernel_vsyscall () (gdb) info threads Id Target Id Frame * 1 Thread 0xb7bbb640 (LWP 3183) "a.out" 0xb7fd2559 in __kernel_vsyscall () 2 Thread 0xb773fb40 (LWP 3187) "ghc_ticker" 0xb7fd2559 in __kernel_vsyscall () 3 Thread 0xb68ffb40 (LWP 3188) "a.out:w" 0xb7fd2559 in __kernel_vsyscall () 4 Thread 0xb5effb40 (LWP 3189) "a.out:w" 0xb7fd2559 in __kernel_vsyscall () 5 Thread 0xb54ffb40 (LWP 3190) "a.out:w" 0xb7fd2559 in __kernel_vsyscall () (gdb) bt #0 0xb7fd2559 in __kernel_vsyscall () #1 0xb7f43ecc in futex_wait_cancelable (private=0, expected=0, futex_word=0x8429dd0) at ../sysdeps/nptl/futex-internal.h:186 #2 __pthread_cond_wait_common (abstime=<optimized out>, clockid=<optimized out>, mutex=<optimized out>, cond=<optimized out>) at pthread_cond_wait.c:508 #3 __pthread_cond_wait (cond=0x8429da8, mutex=0x8429dd8) at pthread_cond_wait.c:638 #4 0x0838a2a0 in waitCondition (pCond=0x8429da8, pMut=0x8429dd8) at rts/posix/OSThreads.c:139 #5 0x0837310d in waitForWorkerCapability (task=<optimized out>) at rts/Capability.c:708 #6 yieldCapability (pCap=<optimized out>, task=<optimized out>, gcAllowed=<optimized out>) at rts/Capability.c:1013 #7 0x0837b4a6 in scheduleYield (task=0x8429da0, pcap=0xbffff408) at rts/Schedule.c:705 #8 schedule ( initialCapability=initialCapability@entry=0x84154c0 <MainCapability>, task=task@entry=0x8429da0) at rts/Schedule.c:315 #9 0x0837c32f in scheduleWaitThread (tso=0xb6a04bd4, ret=0x0, pcap=0xbffff46c) at rts/Schedule.c:2630 #10 0x08372124 in rts_evalLazyIO (cap=<optimized out>, p=<optimized out>, ret=<optimized out>) at rts/RtsAPI.c:566 #11 0x0837dfca in hs_main (argc=<optimized out>, argv=<optimized out>, main_closure=0x83e66cc, rts_config=...) at rts/RtsMain.c:72 #12 0x0806e1f7 in main ()
調べられるのは、ここまでぐらいかな。なお、a.outを走らせる場合、同じ階 層に、lib/ が必要になるんで、 ln -s ../lib lib しておく事。
スレッドで動くのが、普通なんだな。追加した -threadは必須なオプショ ン。-rtsoptsは、実行時にオプションでメモリー割り付け等の変更許 可、-with-rtsopts=-Nは、複数のCPU使用許可か。
cabal build vs ghc –make
cabalはパッケージを作るという目標がある。その為、ghc –makeよりは余分 な事をやってるので、遅くなるはず。どれぐらいの差がある。計測計測ですよ。
sakae@deb:/tmp/cs$ time cabal build > /dev/null real 0m20.176s user 0m18.800s sys 0m1.264s
sakae@deb:/tmp/cs/app$ time sh gen.sh > /dev/null real 0m4.160s user 0m3.662s sys 0m0.483s
これは、全部のファイルをコンパイルした場合だ。特定ファイルを変更した場 合は、そのファイルのみ再コンパイルしてからリンクするはず。ghc –makeは、 そういう挙動をした。それに対して、cabal buildの方は、変更したファイル 以外もコンパイルしてたぞ。どういう、こっちゃ?
ともかく、これならPDCAのサイクルを速く回せるな。なんのこっちゃ。
ghciの:break
ちょっと、追跡してみる。
sakae@deb:/tmp/cs$ cabal repl ghci> :break Prim.numOp Breakpoint 0 activated at app/Prim.hs:124:34-58 Breakpoint 1 activated at app/Prim.hs:125:34-50 Breakpoint 2 activated at app/Prim.hs:126:34-50 Breakpoint 3 activated at app/Prim.hs:127:34-69 Breakpoint 4 activated at app/Prim.hs:128:34-69 Breakpoint 5 activated at app/Prim.hs:129:34-69 ghci> :main "-r"
どれでも、いいよって、マルチなBPなんだな。
Repl> (+ 12 34) Stopped in Prim.numOp, app/Prim.hs:124:34-58 _result :: LispVal.Eval LispVal.LispVal = _ op :: Integer -> Integer -> Integer = _ x :: Integer = 12 y :: Integer = 34 [app/Prim.hs:124:34-58] ghci> :hist Empty history. Perhaps you forgot to use :trace? [app/Prim.hs:124:34-58] ghci> :trace 46
こんな呼出の歴史があるのか。
Repl> (+ 123 456) Stopped in Prim.numOp, app/Prim.hs:124:34-58 _result :: LispVal.Eval LispVal.LispVal = _ op :: Integer -> Integer -> Integer = _ x :: Integer = 123 y :: Integer = 456 [app/Prim.hs:124:34-58] ghci> :hist -1 : eval (app/Eval.hs:163:28-35) -2 : eval (app/Eval.hs:163:28-35) -3 : binopFold (app/Prim.hs:115:39-44) -4 : binopFold (app/Prim.hs:(114,26)-(117,55)) -5 : eval (app/Eval.hs:231:35-49) -6 : eval (app/Eval.hs:163:19-35) -7 : eval (app/Eval.hs:163:19-35) -8 : eval (app/Eval.hs:228:11-22) -9 : getVar (app/Eval.hs:119:18-25) -10 : getVar (app/Eval.hs:118:25-42) -11 : getVar (app/Eval.hs:118:8-43) -12 : getVar (app/Eval.hs:117:17-19) -13 : getVar (app/Eval.hs:(116,22)-(120,40)) -14 : eval (app/Eval.hs:168:19-26) -15 : eval (app/Eval.hs:227:13-18) -16 : updateEnv (app/Eval.hs:241:56-76) -17 : updateEnv (app/Eval.hs:241:43-52) : [app/Prim.hs:124:34-58] ghci> :cont 579
これをやる時は、Exception で設定しておいた、.ghci を無効にしておく事。 変な例外で追跡を乱される。
今度は、楽しいHTTP関係の追跡。
[sakae@fb /tmp/cs]$ cabal repl ghci> :break Prim.openURL Breakpoint 0 activated at app/Prim.hs:(81,13)-(84,31) ghci> :main "-r" Repl> (wslurp "http://localhost:8080/") Stopped in Prim.openURL, app/Prim.hs:(81,13)-(84,31) _result :: IO LispVal.LispVal = _ x :: Data.Text.Internal.Text = Data.Text.Internal.Text (ByteArray# <bytearray>) 0 22 [app/Prim.hs:(81,13)-(84,31)] ghci> :print x x = Data.Text.Internal.Text (ByteArray# <bytearray>) 0 22 [app/Prim.hs:(81,13)-(84,31)] ghci> show x "\"http://localhost:8080/\"" [app/Prim.hs:(81,13)-(84,31)] ghci>
でもこれ、細かすぎて疲れるんだよなー。
[app/Eval.hs:113:3-11] ghci> :hist -1 : openURL (app/Prim.hs:84:3-31) -2 : openURL (app/Prim.hs:83:11-29) -3 : openURL (app/Prim.hs:82:36-45) -4 : openURL (app/Prim.hs:82:23-45) -5 : openURL (app/Prim.hs:82:11-46) -6 : openURL (app/Prim.hs:(81,13)-(84,31)) -7 : wSlurp (app/Prim.hs:87:35-45) -8 : wSlurp (app/Prim.hs:87:24-45) -9 : eval (app/Eval.hs:164:28-35) -10 : unop (app/Prim.hs:61:18-21) -11 : eval (app/Eval.hs:231:35-49) -12 : eval (app/Eval.hs:164:19-35) -13 : eval (app/Eval.hs:228:11-22) -14 : getVar (app/Eval.hs:119:18-25) :
と、贅沢な事を言ってみる。
print debug
上で作成した、gen.shは、まがりなりにもshell script。ならば、続けて、下 記のコードで、アプリを叩いてしまえ。アプリのコードをちら見したら、 stdinからの入力も受け付けてくれるみたいだったから。
echo '(wslurp "http://localhost:8080/")' | \ ./a.out -r
Prim.hsを多少変形した。
openURL x = do mapM_ print ["in openURL: ", x] -- add debug req <- simpleHTTP (getRequest $ T.unpack x) print req -- add debug body <- getResponseBody req return $ String $ T.pack body
doブロックの中なんで、こんなにお手軽だ。ほかの場合は、どうする? ghc –makeの効果は絶大で、変更したファイルだけを再コンパイルして、直ぐに実 行してくれたよ。
/tmp/cs/app/gen.sh Loaded package environment from /home/sakae/.ghc/x86_64-linux-9.2.7/environments/default Repl> "in openURL: " "http://localhost:8080/" Right HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.3 :
埋め込んだ mapM_ なんだけど、咄嗟に思い付いたやつ。だって、"in open" は、String、xの方は、Textという事で、型が合わない。だったら、printにま かせてしまえって発想。これだと惜しいから、出力が2行になってしまう。たまたま 見ていたRWHにヒントが有った。
putStrLn ("in openURL: " ++ (show x))
printが裏で使っているshowが万能なのさ。
ちょいと、お遊びする。
/tmp/cs/app/gen.sh Loaded package environment from /home/sakae/.ghc/i386-linux-9.2.7/environments/default Repl> in openURL: "https://hamesspam.sakura.ne.jp" user error (https not supported) Repl> Goodbye.
即座に拒否された。困った時代だのう。http-conduit: HTTP client package with conduit interface and HTTPS support. こういう物で置き換えなさいって事か。例を参照すると、ひょいと置き換え出 来そうにないな。それより、大変気になった事がある。例を見ると、冒頭が
#!/usr/bin/env stack -- stack script --resolver lts-12.21 {-# LANGUAGE OverloadedStrings #-} import qualified Data.ByteString.Lazy.Char8 as L8 :
のように、stackのスクリプトだよってなってる事。これって、stackを入れた 人は、スクリプトの様に、何も準備しなくても走らせられるって事? 半信半 疑であります。もやもやは、良くないので、すぐに調べる課。
晴天の霹靂。便利すぎる。慣れてしまうと、ChatGPTを使う人のように堕落の沼 に、はまりそうなので、暫くは封印する。
Repl> in openURL: "http://hoge.fuga.foobar.jp" Network.Socket.getAddrInfo (called with preferred socket type/protocol: AddrInf\ o {addrFlags = [], addrFamily = AF_UNSPEC, addrSocketType = Stream, addrProtoco\ l = 0, addrAddress = 0.0.0.0:0, addrCanonName = Nothing}, host name: Just "hoge\ .fuga.foobar.jp", service name: Just "80"): does not exist (Temporary failure i\ n name resolution) Repl> Goodbye.
名前解決に失敗すると、こんなエラーになるのか。simpleHTTP()にソケットが 渡る以前の問題だから、simpleHTTP() のLeftも発動しようも無いって事か。