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を入れた 人は、スクリプトの様に、何も準備しなくても走らせられるって事? 半信半 疑であります。もやもやは、良くないので、すぐに調べる課。

Stack's script interpreter

How to Script with Stack

Haskellでちょっとしたスクリプトを書く

晴天の霹靂。便利すぎる。慣れてしまうと、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も発動しようも無いって事か。

etc

端末上のghciでグラフィック

ghc source git 自分でコンパイルしたら、どれぐらいのリソースが必要になるのかな? 貧乏マシンでは、無理な気がするぞ。


This year's Index

Home