Skip to content

Commit 783c594

Browse files
authored
Merge pull request #180 from haskell-jp/haskell-day-2019
記事の追加: 日本語をshowしてうまく表示されなかったら
2 parents 3718115 + ee175b7 commit 783c594

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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](http://hackage.haskell.org/package/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+
なので、新しい型クラスを定義する必要もなく、そのまま`Show`型クラスのインスタンスを再利用できるのです。
63+
64+
早速先ほどの`show`を使った例に適用してみましょう。
65+
66+
まずは👇のコマンドでインストールして、GHCiを起動します。
67+
68+
```bash
69+
stack build unicode-show
70+
stack exec ghci
71+
72+
# あるいは、最近のcabalを使っている場合は...
73+
cabal v2-install --lib unicode-show
74+
cabal v2-repl -b unicode-show
75+
```
76+
77+
`Text.Show.Unicode`モジュールを`import`して`show`を使っている箇所を`ushow`に変えれば、お望みどおりの挙動になります。
78+
79+
```haskell
80+
ghci> import Text.Show.Unicode
81+
ghci> iimashita x = "今、" ++ ushow x ++ "って言いました!?"
82+
ghci> putStrLn (iimashita "ハスケル")
83+
"ハスケル"って言いました!?
84+
```
85+
86+
わくわくもんですね!
87+
88+
`print`の例も、`uprint`に変えれば🆗です。
89+
90+
```haskell
91+
ghci> uprint "私、堪忍袋の緒が切れました!"
92+
"私、堪忍袋の緒が切れました!"
93+
```
94+
95+
ウルトラハッピーですね!!
96+
97+
さらに、次のコマンドをGHCiに入力すれば、GHCiに直接入力した日本語文字列もそのまま表示されるようになります。
98+
99+
```haskell
100+
ghci> :set -interactive-print=uprint
101+
ghci> "みんなで幸せゲットだよ!"
102+
"みんなで幸せゲットだよ!"
103+
```
104+
105+
カンペキ✨
106+
107+
えっ、常に`uprint`したいからいちいち`:set -interactive-print=uprint`するのが面倒くさい?
108+
そんなあなたは👇を`~/.ghci`に書くことけって~いでしょう。
109+
110+
```haskell
111+
import qualified Text.Show.Unicode
112+
:set -interactive-print=Text.Show.Unicode.uprint
113+
```
114+
115+
# unicode-showの(ものすごく近い)将来
116+
117+
そんなunicode-showですが、残念ながら一昨年、作者である村主崇行さんが亡くなってしまいました[^nushio]
118+
日本に住むHaskellerをサポートする日本Haskellユーザーグループとしては、このパッケージをメンテナンスし続けることに大きな意義があると判断し、私はこのパッケージを[Haskell-jp](https://github.com/haskell-jp/)のGitHubリポジトリーでメンテナンスすることにしました。
119+
以下がそのリポジトリーです。
120+
121+
[^nushio]: 村主崇行さんは「[すごいHaskellたのしく学ぼう!](https://shop.ohmsha.co.jp/shopdetail/000000001926/)」の翻訳を担当されるなど、unicode-show以外にも日本のHaskell界に多大な功績をもたらした方でした。
122+
123+
<https://github.com/haskell-jp/unicode-show>
124+
125+
といっても、メンテナーの名前や`LICENSE`ファイルを書き換えて最新版をアップロードして以降特に何もしていなかったのですが<small>([バグはあるけど直すのも難しそう](https://github.com/nushio3/unicode-show/issues/2)だし、概ね使えるし)</small>、なんと先日、Pull requestが来ました!
126+
127+
[Do not show values eagerly by Kaiepi · Pull Request #4 · haskell-jp/unicode-show](https://github.com/haskell-jp/unicode-show/pull/4)
128+
129+
この修正を適用する前のunicode-showは、文字列全体を評価してからエスケープシーケンスを元に戻す、という挙動だったため、長い文字列を与えた場合や無限の長さの文字列を与えた場合に、なかなか<small>(あるいは永遠に)</small>結果が返ってこないという問題がありました。
130+
131+
```haskell
132+
ghci> uprint (repeat "ああああ!")
133+
-- 何も表示されず、Ctrl + C を押すまで処理が返らない
134+
```
135+
136+
修正後はちゃんと遅延評価を利用することで、無限の長さの文字列でも少しずつ変換することができます。
137+
138+
```haskell
139+
ghci> uprint (repeat "ああああ!")
140+
["ああああ!","ああああ!", ... "ああああ!","ああInterrupted.
141+
-- Ctrl + Cを押すまで出力し続ける
142+
```
143+
144+
今日記事にした一番の理由はこの話をするためです。
145+
[Kaiepi](https://github.com/Kaiepi)さんありがとうございます!
146+
先ほどリリースしました!🎉
147+
148+
<http://hackage.haskell.org/package/unicode-show-0.1.0.4>
149+
150+
# (番外編)pretty-simpleも使おう
151+
152+
時間がないので詳しくは省略しますが、実は[pretty-simple](http://hackage.haskell.org/package/pretty-simple)というパッケージを使えば、日本語をそのまま出力するのに加えて、プリティープリントできます。
153+
154+
```haskell
155+
ghci> import Text.Pretty.Simple
156+
ghci> pPrint ["きーらーめーくー", "ほーしーの力でー", "あこがーれのー", "わーたーし描くよー"]
157+
[ "きーらーめーくー"
158+
, "ほーしーの力でー"
159+
, "あこがーれのー"
160+
, "わーたーし描くよー"
161+
]
162+
```
163+
164+
例ではわかりづらいですが、ちゃんと色も着けてくれます!
165+
それでは2020年も、Happy Haskell Hacking🎁

0 commit comments

Comments
 (0)