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パッケージのデータを、まとめてひとつの文字列としたい。普通にや ると、エラーになる。解決策はないものか?

Multi-line strings in Haskell

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

畳込関数fold:foldrとfoldlの違い

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とWebデザイン魔法の教科書

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みたいな外 部サーバーと、どうやって対決したらいいの?


This year's Index

Home