Skip to content

Commit 1b0af75

Browse files
committed
Add unicode-show.md
1 parent ebc2658 commit 1b0af75

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
---
2+
title: 日本語をshowしてうまく表示されなかったら
3+
subHeading: unicode-showの紹介(と、pretty-simpleを少し)
4+
headingBackgroundImage: ../../img/background.png
5+
headingDivClass: post-heading
6+
author: YAMAMOTO Yuji
7+
postedBy: <a href="http://the.igreque.info/">YAMAMOTO Yuji(@igrep)</a>
8+
date: December 22, 2019
9+
tags: 日本語
10+
...
11+
---
12+
13+
# ℹ️この記事は
14+
15+
この記事は、[Haskell Advent Calendar 2019](https://qiita.com/advent-calendar/2019/haskell) 22日目の記事です。
16+
例年どおりタイプセーフプリキュア!の話をするつもりでしたが、ネタが実装できなかったのでunicode-showの話をします[^precure]
17+
まぁ、こちらの方がみなさんにとっては有益でしょうし🙃
18+
19+
[^precure]: 例年どおりですとプリキュアAdvent Calendarと同時投稿をしている予定でしたが、例年参加者が減っていたこともあり、今年はプリキュアAdvent Calendarはなくなってしまいました😞
20+
21+
# 🇯🇵日本語(などの)話者がHaskellを始めるとあるある
22+
23+
GHCiに日本語を入力したり...
24+
25+
```haskell
26+
ghci> "みんなで幸せゲットだよ!"
27+
"\12415\12435\12394\12391\24184\12379\12466\12483\12488\12384\12424\65281"
28+
```
29+
30+
日本語を`print`したり...
31+
32+
```haskell
33+
ghci> print "私、堪忍袋の緒が切れました!"
34+
"\31169\12289\22570\24525\34955\12398\32210\12364\20999\12428\12414\12375\12383\65281"
35+
```
36+
37+
日本語を`show`したり...
38+
39+
```haskell
40+
ghci> iimashita x = "今、" ++ show x ++ "って言いました!?"
41+
ghci> putStrLn (iimashita "ハスケル")
42+
"\12495\12473\12465\12523"って言いました!?
43+
```
44+
45+
日本語の大半が変な文字列に変わってしまいました😥。
46+
47+
へ...、変な文字列じゃないし!エスケープシーケンスに変換しただけだから!
48+
49+
これは、Haskell標準における`show`関数の残念な仕様です。
50+
`show`関数に文字列を渡すと、ダブルクォートで囲った上で、ASCII範囲外の文字列や、ASCIIの非表示文字などをエスケープシーケンスに変換して返します。
51+
これは、`show`関数をデバッグで使用した際、指定した文字列にどんな文字が含まれているか、簡単にわかるようにするための仕様です。
52+
文字の文字コードを表示すれば、例えばNULL文字や制御文字にゼロ幅文字、特殊なスペースなど、視認しにくいおかしな文字が含まれていても、一目でわかるのです。
53+
54+
しかしこれは日本語話者である我々にとって、少なくとも日本語の文字に関しては「余計なお世話」です。
55+
NULL文字やASCIIの制御文字といった本来画面に表示することがない文字列ならともかく、ASCII範囲外の文字列すべてをエスケープしてしまうのはやり過ぎでしょう。
56+
現代はUnicodeがあるおかげで、日本語に限らずともASCII範囲外の文字を扱うのは当たり前になりましたから。
57+
58+
# unicode-showを使おう
59+
60+
そこで便利なのが[unicode-show](http://hackage.haskell.org/package/unicode-show)です。
61+
unicode-showの`ushow`関数は、`show`がエスケープシーケンスに変換した日本語などの文字列を元の文字列に戻してくれるのです。
62+
63+
早速先ほどの`show`を使った例に適用してみましょう。
64+
65+
まずは👇のコマンドでインストールして、GHCiを起動します。
66+
67+
```bash
68+
stack build unicode-show
69+
stack exec ghci
70+
71+
# あるいは、最近のcabalを使っている場合は...
72+
cabal v2-install --lib unicode-show
73+
cabal v2-repl -b unicode-show
74+
```
75+
76+
`Text.Show.Unicode`モジュールを`import`して`show`を使っている箇所を`ushow`に変えれば、お望みどおりの挙動になります。
77+
78+
```haskell
79+
ghci> import Text.Show.Unicode
80+
ghci> iimashita x = "今、" ++ ushow x ++ "って言いました!?"
81+
ghci> putStrLn (iimashita "ハスケル")
82+
"ハスケル"って言いました!?
83+
```
84+
85+
わくわくもんですね!
86+
87+
`print`の例も、`uprint`に変えれば🆗です。
88+
89+
```haskell
90+
ghci> uprint "私、堪忍袋の緒が切れました!"
91+
"私、堪忍袋の緒が切れました!"
92+
```
93+
94+
ウルトラハッピーですね!!
95+
96+
さらに、次のコマンドをGHCiに入力すれば、GHCiに直接入力した日本語文字列もそのまま表示されるようになります。
97+
98+
```haskell
99+
ghci> :set -interactive-print=uprint
100+
ghci> "みんなで幸せゲットだよ!"
101+
"みんなで幸せゲットだよ!"
102+
```
103+
104+
カンペキ✨
105+
106+
えっ、常に`uprint`したいからいちいち`:set -interactive-print=uprint`するのが面倒くさい?
107+
そんなあなたは👇を`~/.ghci`に書くことけって~いでしょう。
108+
109+
```haskell
110+
import qualified Text.Show.Unicode
111+
:set -interactive-print=Text.Show.Unicode.uprint
112+
```
113+
114+
# unicode-showの(ものすごく近い)将来
115+
116+
そんなunicode-showですが、残念ながら一昨年、作者である村主崇行さんが亡くなってしまいました[^nushio]
117+
日本に住むHaskellerをサポートする日本Haskellユーザーグループとしては、このパッケージをメンテナンスし続けることに大きな意義があると判断し、私はこのパッケージを[Haskell-jp](https://github.com/haskell-jp/)のGitHubリポジトリーでメンテナンスすることにしました。
118+
以下がそのリポジトリーです。
119+
120+
[^nushio]: 村主崇行さんは「[すごいHaskellたのしく学ぼう!](https://shop.ohmsha.co.jp/shopdetail/000000001926/)」の翻訳を担当されるなど、unicode-show以外にも日本のHaskell界に多大な功績をもたらした方でした。
121+
122+
<https://github.com/haskell-jp/unicode-show>
123+
124+
といっても、メンテナーの名前や`LICENSE`ファイルを書き換えて最新版をアップロードして以降特に何もしていなかったのですが<small>(バグはあるけど直すのも難しそうだし、概ね使えるし)</small>、なんと先日、Pull requestが来ました!
125+
126+
[Do not show values eagerly by Kaiepi · Pull Request #4 · haskell-jp/unicode-show](https://github.com/haskell-jp/unicode-show/pull/4)
127+
128+
この修正前のunicode-showは、文字列全体を評価してからエスケープシーケンスを元に戻す、という挙動だったため、長い文字列を与えた場合や無限の長さの文字列を与えた場合に、なかなか<small>(あるいは永遠に)</small>結果が返ってこないという問題がありました。
129+
130+
```haskell
131+
ghci> uprint (repeat "ああああ!")
132+
-- Ctrl + C を押すまで結果が返ってこない
133+
```
134+
135+
修正後はちゃんと遅延評価を利用することで、無限の長さの文字列でも少しずつ変換することができます。
136+
137+
```haskell
138+
ghci> uprint (repeat "ああああ!")
139+
["ああああ!","ああああ!", ... "ああああ!","ああInterrupted.
140+
```
141+
142+
今日記事にした一番の理由はこの話をするためです。
143+
[Kaiepi](https://github.com/Kaiepi)さんありがとうございます!
144+
警告を終始していただけたらマージしてリリースします。
145+
146+
# (番外編)pretty-simpleも使おう
147+
148+
時間がないので詳しくは省略しますが、実は[pretty-simple](http://hackage.haskell.org/package/pretty-simple)というパッケージを使えば、日本語をそのまま出力するのに加えて、プリティープリントできます。
149+
150+
```haskell
151+
ghci> import Text.Pretty.Simple
152+
ghci> pPrint ["きーらーめーくー", "ほーしーの力でー", "あこがーれのー", "わーたーし描くよー"]
153+
[ "きーらーめーくー"
154+
, "ほーしーの力でー"
155+
, "あこがーれのー"
156+
, "わーたーし描くよー"
157+
]
158+
```
159+
160+
例ではわかりづらいですが、ちゃんと色も着けてくれます!
161+
それでは2020年も、Happy Haskell Hacking🎁

0 commit comments

Comments
 (0)