Cabal package search
Table of Contents
RWH
Real World Haskell free book
Real World Haskell Source Code
Real World Haskell の古いところ あの方のお墨付き
この本も眼を通す。そしたら、josnを題材に、pretty-printのライブラリィー を開発する実例がでてた。prettyって興味あるな。それからjsonも下記のよう な実例があるな。
sakae@deb:~/src/cabal-catlog/Rasterific/0.7.5.4$ cat package.json {"signatures":[],"signed":{"_type":"Targets","expires":null,"targets":{"<repo>/package/Rasterific-0.7.5.4.tar.gz":{"hashes":{"md5":"64f96bb70768d1ae8597fd23fba95de8","sha256":"c1156b67e8314522448449de7815fdfc815b5ee6b616baf9c17d16b484a3511f"},"length":827372}},"version":0}}
2つの材料を発見するとなく、発見してしまったわけだ。で、自習材料を考え てみた。
cabal catalog
どんなパッケージが提供されてるかは、下記の様にして調査できる。prettyを 含むものをリスト、おのおののヘッダー部分だけを表示。目当てがあったら、 infoで詳細を調査。
[sakae@fb /tmp]$ cabal list pretty | grep '^*' [sakae@fb /tmp]$ cabal info show-prettyprint
これで十分なんだけど、あえてアプリを作成してみる。上記のcabal listでは、 一行に一パッケージが表示されちゃって、視認性が悪い。インタラクティブに lsすると、一行に複数が表示される。真似してみたい。
tar tvf 01-index.tar | awk '{print $6}' | cut -d '/' -f 1 | sort | uniq >seed
インディクスは、31M があったけど、畳みこむと 214K になった。パッケージ 数は、17112個だった。前回tarの結果だけ見て、とんでもないなと早合点した のは恥しい事だ。 (FreeBSDの場合は、$6 -> $9 にする)
んで、このseedなファイルをreadしてくのが、普通だろうけど、それだとアプ リとデータが分離しちゃう。ハロワのアプリだけでも、そのサイズが3Mにもな るなら、データもアプリ内にとりこんでしまいたい。本当のシングルバイナリー ね。
それから、どうでもいい事だけど、密かな疑問。ファイルの時刻がほぼ同一だ けど、どゆ事? 普通に考えれば、01-index.tar.gz をダウンロードしてきて、 後は展開、加工って流れになると思う。でも、tar玉の展開には、とんでもな い時間とDISK容量が必要。具体的には 1.8G。そんなのやってる素振りは全く なかったぞ。
[sakae@fb ~/.cache/cabal/packages/hackage.haskell.org]$ ls -l 01* -rw-r--r-- 1 sakae wheel 7299488 May 10 05:50 01-index.cache -rw-r--r-- 1 sakae wheel 849989632 May 10 05:49 01-index.tar -rw-r--r-- 1 sakae wheel 113534642 May 10 05:49 01-index.tar.gz -rw-r--r-- 1 sakae wheel 5034008 May 10 05:49 01-index.tar.idx -rw-r--r-- 1 sakae wheel 4 May 10 05:49 01-index.timestamp
Multi-line strings
一行に1パッケージのデータを、まとめてひとつの文字列としたい。普通にや ると、エラーになる。解決策はないものか?
main = putStrLn seed seed :: String seed = "3dmodels\ \ 4Blocks\ \ AX\ \ AAI"
"" の周りはバックスリャッシュが不要なんだな。これは、通常1行で記述す べきって言うlexerの都合なん だな。だから、seed = "python\nruby\n" は、問題なし。物理的改行があると、上記のようなマジックが必要とな。
で、 そんなのがsedで簡単に作成出来るか? とかく記号がからむと面倒だから。
#! /bin/sh cat lang.txt | sed 's!\(.*\)!\\ \1\\!' [sakae@arch q]$ sh cv.sh \ python\ \ ruby\ \ haskell\ \ rust\ \ lisp\ \ scheme\
どうやら大丈夫だった。文字のバックスリャッシュは、2つ重ねてくださいっ てのは、お約束です。
cabal install
次は、アプリのインストール体験をしておく。
[sakae@arch t]$ cabal install Wrote tarball sdist to /tmp/t/dist-newstyle/sdist/t-0.1.0.0.tar.gz Resolving dependencies... Build profile: -w ghc-9.2.7 -O1 In order, the following will be built (use -v for more details): - t-0.1.0.0 (exe:t) (requires build) Starting t-0.1.0.0 (exe:t) Building t-0.1.0.0 (exe:t) Installing t-0.1.0.0 (exe:t) Completed t-0.1.0.0 (exe:t) Symlinking 't' to '/home/sakae/.cabal/bin/t' [sakae@arch ~]$ ls -l .cabal/bin/t lrwxrwxrwx 1 sakae sakae 103 May 12 14:32 .cabal/bin/t -> ../store/ghc-9.2.7/t-0.1.0.0-e-t-744b7935de67a1638e9f209e08970e1a81cfbff4f0108d374393482eee6e2aea/bin/t
リンクファイルになるんだ。次はちょっと変更したので、再インストール。
Symlinking 't' to '/home/sakae/.cabal/bin/t' cabal: Path '/home/sakae/.cabal/bin/t' already exists. Use --overwrite-policy=always to overwrite. [sakae@arch t]$ cabal install --overwrite-policy=always Symlinking 't' to '/home/sakae/.cabal/bin/t'
普通は再インストール禁止。どうしてもやりたかったら、オプションを付けろ。 結果は、再リンクしたからね。という事は、古い実体がそのまま放置されてるっ て事です。誰にも使われなく。haskell流と言ってしまえばそれまでだけど、 いやらしいです。GCを利用者に負担させてます。大いなる注意が必要です。
hoogle search
文字列の中に文字列が含まれているか知りたい。型を頼りに検索。
:: String -> String -> Bool isInfixOf :: String -> String -> Bool basement Basement.String Check whether the first string is contains within the second string. :: [Char] -> [Char] -> Bool isInfixOf :: Eq a => [a] -> [a] -> Bool base Data.List GHC.OldList The isInfixOf function takes two lists and returns True iff the first list is contained, wholly and intact, anywhere within the second.
baseで提供されてるなら、何も追加する必要は無い。
module Main where import Data.List (isInfixOf) main :: IO () main = print $ filter (isInfixOf "u") $ words seed seed :: String seed = "python\ \ ruby\ \ haskell\ \ rust\ \ lisp\ \ scheme"
最終的なアプリの太り方 3524424 -> 3746432 追加した文字列分だけ、サイズ が増加している。それから、この巨大なhsファイルを編集しようとすると、 emacsが激遅くなる。haskell-modeがしっかりチェックしてる為だ。 haskell-modeを無効に出来ればいいんだけど、それもままならない。一時的にサフィックス を変更すべき。まあ、cat Main.hs seed >tmp; mv tmp Main.hsは完成した時 にやればよい。emacsやめて、ちょっとばかしvi使えと影の声。そんな訳で、debug用に少し進歩させた。
module Main where import Data.List (isInfixOf) main :: IO () main = do let seld = pick "aa" let mlon = foldl max 0 $ map length seld print mlon pick :: [Char] -> [[Char]] pick pat = filter (isInfixOf pat) $ words seed seed :: String seed = "a aa aaa aaaa b bb bbb bbba abc abcba"
hlint
端末のcolsと最長パッケージ名を考慮して、一行に表示するパッケージ数を決 定。それに基き、パッケージの組を作成したい。直に思いつくのは、takeだな。 型をみると、take :: Int -> [a] -> [a] これだと、最初のやつしか取れない。 本当に欲しいのは、 Int -> [a] -> [[a]\] 検索すると、splitと言うピッタ リな物が見付かる。但し、パッケージを追加しろとな。それは、避けたい。 splitAt :: Int -> [a] -> ([a], [a]) が使えそうなんで、強引にやってみた。
split :: Int -> [a] -> [[a]] split n [] = [] split n a = fst (splitAt n a) : (split n (snd (splitAt n a)))
違和感が満載。括弧の使い過ぎ。2度もsplitAtしてて無駄。巷でも、割り算の 商と余りを同時にgetするdivmodなんてのが用意されてるからなあ。
そうだ、ChatGPTに相談すれば、いいんでないかい。そんな事もあろうかと、 haskell専用のChatGPTが用意されている。使わない手は無い。
[sakae@fb /tmp/t/app]$ hlint Main.hs Main.hs:15:14-30: Warning: Use take Found: fst (splitAt n a) Perhaps: take n a Main.hs:15:34-62: Suggestion: Redundant bracket Found: fst (splitAt n a) : (split n (snd (splitAt n a))) Perhaps: fst (splitAt n a) : split n (snd (splitAt n a)) Main.hs:15:44-60: Warning: Use drop Found: snd (splitAt n a) Perhaps: drop n a 3 hints
これにインスパイアーされて、下記の判決文を書いた。
split :: Int -> [a] -> [[a]] split n [] = [] split n xs = hs : split n ts where (hs, ts) = splitAt n xs
主文は、split n xs = の部分。副文はwhere以下の部分ね。ChatGPTも余り頼 りにならないな。
getArgs
いよいよ、外界から検索文字列を注入するって事で、getArgsを導入してみた。 開発環境のghci in emacsで、少し失敗した事があるんで載せておく。
λ> :main "aa" [["aa","aaa"],["aaaa","aaaaa"],["aaaaaa"]] λ> :main "b" [["b","bb"],["bbb","bbba"],["abc","abcba"]]
これが、正しい使い方
λ> main aa <interactive>:13:6: error: Variable not in scope: aa
ありがちな失敗 その壱
λ> main "aa" <interactive>:14:1: error: • Couldn't match expected type: String -> t with actual type: IO () • The function ‘main’ is applied to one value argument, but its type ‘IO ()’ has none In the expression: main "aa" In an equation for ‘it’: it = main "aa" • Relevant bindings include it :: t (bound at <interactive>:14:1)
ありがちな失敗 その弐
λ> main Stopped in <exception thrown>, <unknown> _exception :: e = _ [<unknown>] λ> :quit -- ask restart by emacs λ>
ありがちな重症的失敗。これが一番多いと思う。例外が発生すると、ghciのレ ベルが遷移するんで、そこから脱出しないと、どうしようもなくなる。なんか、 こんなのが lispの開発環境でも有ったな。allegro common lispだったような、 遥か昔の事。
[sakae@fb /tmp/t]$ cabal run -- t aa [["aa","aaa"],["aaaa","aaaaa"],["aaaaaa"]]
こちらは、cabal run すなわち、インストールしないで確認する方法だ。t は パッケージ名ね。我乍ら、ものぐさな名前だな。
foldl and foldr
consとappendの違いって事だな。
自前でlambda式を書く時は注意。2引数だからね。これに嵌った。
λ> :t foldl foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b λ> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
adj n x = replicate (n - length x) '-' ++ x λ> foldl (\t s -> adj 5 s ++ t) "" ["abc", "s"] "----s--abc" λ> foldr (\s t -> adj 5 s ++ t) "" ["abc", "s"] "--abc----s"
cabal package search
とりあえずテストプラントで動かしてみた。ターミナルの横幅は短くしてる。 そう、RWHのp 137の例のように。本番ではスペースとする所を、今はマイナス 文字にしている。また綺麗なsplitのコードが、見るも無惨に改変されてて、 とてもsplit' ぐらいな名前変更では済みそうにないな。
module Main where import System.Environment import Data.List (isInfixOf) main :: IO () main = do (pat:_) <- getArgs let seld = filter (isInfixOf pat) $ words seed -- grep let mlon = 1 + (foldl max 0 $ map length seld) -- longst word length + 1 let lns = split mlon (cols `div` mlon) seld -- [line] mapM_ print lns split :: Int -> Int -> [[Char]] -> [[Char]] split z n [] = [] split z n xs = aa : split z n ts where (hs, ts) = splitAt n xs aa = foldr (\s t -> pad z s ++ t) "" hs pad n x = replicate (n - length x) '-' ++ x cols = 14 -- terminal cols width seed :: [Char] seed = "1 12 123 1234 12345 a aa aaa aaaa b bb bbb bbba abc"
実行例。右揃えにして、自己主張してみた。
[sakae@fb /tmp/t]$ cabal run -- t 1 "-----1----12" "---123--1234" "-12345" [sakae@fb /tmp/t]$ cabal run -- t b "----b---bb" "--bbb-bbba" "--abc"
あれ、printだと、文字列ってのが、あからさまに分ってしまうなあ。debug中 は、機械よりのprintを使う。最後は、人間寄りな、putStrLnにする。
折角なので、実データでも確認。
sakae@deb:~$ t debug debug debug-diff debug-dump debugger-hs debug-me debug-pp debug-time debug-trace-var debug-tracy ghc-debug-brick ghc-debug-client ghc-debug-common ghc-debug-convention ghc-debug-stub gogol-debugger haskell-debug-adapter haskell-tools-debug KiCS-debugger print-debugger remote-debugger Shpadoinkle-debug smtlib2-debug titan-debug-yampa tlex-debug tls-debug sakae@deb:~$ t trace bindings-potrace category-traced contra-tracer contra-tracers debug-trace-var dtrace ghc-trace-events gogol-cloudtrace graph-trace graph-trace-dot graph-trace-viz haskell-stack-trace-plugin hpc-tracer htrace : sakae@deb:~$ t pretty : prettyprinter-combinators prettyprinter-compat-annotated-wl-pprint prettyprinter-compat-ansi-wl-pprint prettyprinter-compat-wl-pprint :
病的に長い名称のパッケージが有るなあ。
bug ? and TODO
sakae@deb:~$ t t: user error (Pattern match failure in 'do' block at app/Main.hs:7:3-9)
引数を与えなかった時のメッセージ。これはbugですか? それとも仕様ですか? MSに習って、仕様で押し通したいぞ。だって、それなりのメッセージが出てい るから。
TODOとして、端末の桁数に自動追従させたいな。これ、通常は80桁で使ってい るんだけど、行儀の悪い横にビローンと延びてるやつなら最大化して、126桁 にする事がある。stty -a | grep columns 相当を組み込めが実現できるな。
xmonad
恐しい名前のウィンドウ・マネージャである。 Would you like Xmonad in the fashonable mood
結合の強さについて探していたんよ。 haskellの関数をおしゃれに把握する それで見付けたんだ。
for FreeBSD
[sakae@fb /usr/ports]$ make search key=xmonad Port: hs-xmonad-0.17.1 Path: /usr/ports/x11-wm/hs-xmonad Info: Tiling window manager Maint: haskell@FreeBSD.org B-deps: ghc-9.2.7_1 gmp-6.2.1 hs-cabal-install-3.10.1.0_1 indexinfo-0.3.1 libX11-1.7.2,1 libXScrnSaver-1.2.4 libXau-1.0.9 libXdmcp-1.1.3 libXext-1.3.4,1 libXinerama-1.1.4_2,1 libXrandr-1.5.2 libXrender-0.9.10_2 libffi-3.4.4 libxcb-1.15_1 ncurses-6.4 xorgproto-2022.1 R-deps: ghc-9.2.7_1 gmp-6.2.1 indexinfo-0.3.1 libX11-1.7.2,1 libXScrnSaver-1.2.4 libXau-1.0.9 libXdmcp-1.1.3 libXext-1.3.4,1 libXinerama-1.1.4_2,1 libXrandr-1.5.2 libXrender-0.9.10_2 libffi-3.4.4 libxcb-1.15_1 ncurses-6.4 xorgproto-2022.1 WWW: https://xmonad.org/
でも、 archLinux xmonad でも、提供されてる。
xmonad - Lightweight X11 window manager written in Haskell
勿論、Debianにもある。
こちらは、アドオンになるのかな。
sakae@deb:~$ t xmonad ixmonad xmonad xmonad-bluetilebranch xmonad-contrib xmonad-contrib-bluetilebranch xmonad-contrib-gpl xmonad-dbus xmonad-entryhelper xmonad-eval xmonad-extras xmonad-screenshot xmonad-spotify xmonad-utils xmonad-vanessa xmonad-volume xmonad-wallpaper xmonad-windownames
etc
定番のデバッグ・調査ツールであるstraceでエラーインジェクション
*BSDな ktrace/kdumpでは、太刀打ちできないな。strace恐るべし。
HTMLは骨格、CSSはファッション、JavaScriptは筋肉だそうです。実にわかり やすい説明。世界初のCERNのWebPageをディスってるけど、オイラーみたいな 人は、それで十分。
だって、Haskell Hierarchical Libraries の、醜い色使いはなんだ。白黒と はっきりした色使いをしてくれ。
そんなの、w3mとかewwを使えば解決するよ。でも、これらは、筋肉マンじゃな いから、筋肉に頼ったレイアウトは絶望的なんだ。
だったら、自分で色補正をできないか、調べてみろって事で、最新事情を調べ てみる気になったのさ。
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ... x.x.x.x - - [16/May/2023 06:07:52] "GET / HTTP/1.1" 200 - x.x.x.x - - [16/May/2023 06:07:53] "GET /linuwial.css HTTP/1.1" 200 - x.x.x.x - - [16/May/2023 06:07:53] "GET /quick-jump.css HTTP/1.1" 200 - x.x.x.x - - [16/May/2023 06:07:53] "GET /haddock-bundle.min.js HTTP/1.1" 200 - x.x.x.x - - [16/May/2023 06:07:54] code 404, message File not found x.x.x.x - - [16/May/2023 06:07:54] "GET /doc-index.json HTTP/1.1" 404 - x.x.x.x - - [16/May/2023 06:07:54] code 404, message File not found x.x.x.x - - [16/May/2023 06:07:54] "GET /favicon.ico HTTP/1.1" 404 -
名前からして怪しそうなのは、linuwial.cssだな。色は色々あれど、どれが醜 い色だ。担当色は、firefoxの開発ツールあたりで調べられるのかな。で、ど うやって、その色を注入する? ローカル色ならまだしも、hoogleみたいな外 部サーバーと、どうやって対決したらいいの?