看看这个问题:通常,在交互式Haskell环境中,非拉丁文Unicode字符(构成结果的一部分)被转义打印,即使区域设置允许这样的字符(而不是通过putStrLn
,putChar
直接输出,这看起来很好可读)-示例显示了GHCi和Hugs 98:
$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> "hello: привет"
"hello: \1087\1088\1080\1074\1077\1090"
Prelude> 'Я'
'\1071'
Prelude> putStrLn "hello: привет"
hello: привет
Prelude> :q
Leaving GHCi.
$ hugs -98
__ __ __ __ ____ ___ _________________________________________
|| || || || || || ||__ Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__|| __|| Copyright (c) 1994-2005
||---|| ___|| World Wide Web: http://haskell.org/hugs
|| || Bugs: http://hackage.haskell.org/trac/hugs
|| || Version: September 2006 _________________________________________
Hugs mode: Restart with command line option +98 for Haskell 98 mode
Type :? for help
Hugs> "hello: привет"
"hello: \1087\1088\1080\1074\1077\1090"
Hugs> 'Я'
'\1071'
Hugs> putStrLn "hello: привет"
hello: привет
Hugs> :q
[Leaving Hugs]
$ locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
$
字符串
我们可以猜测,这是因为print
和show
被用来格式化结果,这些函数尽可能以规范的、最大限度的可移植方式格式化数据--所以它们更喜欢转义奇怪的字符(也许,它甚至在Haskell的标准中被拼写出来):
$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> show 'Я'
"'\\1071'"
Prelude> :q
Leaving GHCi.
$ hugs -98
Type :? for help
Hugs> show 'Я'
"'\\1071'"
Hugs> :q
[Leaving Hugs]
$
型
但如果我们知道如何破解GHCi或Hugs以人类可读的方式打印这些字符,即直接,未转义,这将是很好的。当使用交互式Haskell环境用于教育目的时,这可以得到赞赏,用于在非英语观众面前演示Haskell的教程/演示,你想用他们的人类语言展示一些Haskell的数据。
实际上,它不仅对教学很有用,对调试也很有用!当你的函数是在表示其他语言的字符串上定义的,而这些字符串是非ASCII字符。所以,如果程序是特定于语言的,并且只有其他语言的字符才有意义,而你的函数只在这些字符上定义,那么在GHCi中调试时看到这些数据是很重要的。
**总结一下我的问题:**有什么方法可以破解现有的交互式Haskell环境,以便在结果中更友好地打印Unicode?(在我的情况下,“更友好”意味着更“简单”:我希望GHCI或Hugs中的print
能够像putChar
,putStrLn
那样简单直接地显示非拉丁字符,即未转义。
(也许,除了GHCi和Hugs 98之外,我还将研究一下现有的Emacs与Haskell交互的模式,看看它们是否可以以漂亮的、非转义的方式呈现结果。
8条答案
按热度按时间js81xvg61#
一种破解方法是将GHCi Package 到一个shell Package 器中,该 Package 器读取其标准输出并取消转义Unicode字符。当然,这不是Haskell的方法,但它可以完成这项工作:)
例如,这是一个使用
sh
和python3
的 Package 器ghci-esc
(3在这里很重要):字符串
ghci-esc
的用法:型
请注意,并不是上面所有的unescaping都正确完成了,但这是一种向观众显示Unicode输出的快速方法。
wkftcu5l2#
这个问题已经取得了一些进展;感谢bravit(Vitaly Bragilevsky)!:
可能已纳入GHC 7.6.1。(是吗?..)
如何让它现在打印西里尔文:
传递给GHCi的参数应该是一个可以打印西里尔字母的函数。在Hackage上没有找到这样的函数。所以,我们必须创建一个简单的 Package 器,就像现在一样:
字符串
运行
ghci
:ghci -interactive-print=UPPrinter.upprint UPPrinter
当然,这可以一次性地写进
.ghci
中。实际问题:提出一个替代的nice
Show
所以,现在有一个实际问题:用什么来代替标准
Show
(标准Show
违背我们的意愿,避免了想要的符号)?使用他人的作品:其他漂亮的打印机
上面建议使用
Text.PrettyPrint.Leijen
,可能是因为已知字符串中不转义这样的符号。基于Show的自有Show --吸引人,但不实用
我们自己写
Show
怎么样,比如说,ShowGhci
,就像这里的回答中建议的那样。它实用吗?保存为替代
Show
类定义示例的工作(像ShowGhci
),人们可能会试图默认使用Show
的现有示例,只为String
和Char
重新定义示例。但这行不通,因为如果使用showGhci = show
,然后对于任何包含字符串的复杂数据show
都是“硬编译”的,以调用旧的show
来显示字符串。这种情况要求能够将实现相同类接口的不同字典传递给使用此接口的函数(show
会把它传递给子show
s)。对此有任何GHC扩展吗?基于
Show
并只想重新定义Char
和String
的示例是不太实际的,如果你想让它像Show
一样“通用”(广泛适用)的话。重新解析
show
一个更实际(简短的)解决方案是这里的另一个答案:解析
show
的输出以检测字符和字符串,并重新格式化它们。(虽然在语义上看起来有点丑陋,但在大多数情况下,解决方案是简短和安全的(如果show
中没有用于其他目的的引号;对于标准的东西来说肯定不是这样,因为show
的想法是或多或少正确地解析Haskell。程序中的语义类型
还有一句话
实际上,如果我们关心GHCi中的调试,(而不是简单地演示Haskell并希望有一个漂亮的输出),显示非ASCII字母的需要必须来自这些字符在程序中的某些固有存在(否则,为了调试,你可以用拉丁字符代替它们,或者不太关心是否显示代码)。换句话说,从问题域的Angular 来看,这些字符或字符串中有一些含义。(例如,我最近一直在从事俄语的语法分析,而作为示例词典一部分的俄语单词是“固有的”,它的工作只有用这些特定的词才有意义。所以我需要在调试时阅读它们。)
但是看,如果字符串有一些MEANING,那么它们就不再是普通的字符串了;它是有意义类型的数据。如果你为这种意义声明一个特殊的类型,程序可能会变得更好更安全。
然后,万岁!,您只需为该类型定义
Show
的示例,就可以在GHCi中调试程序了。作为一个例子,在我的语法分析程序中,我做了:
型
和
型
(the这里额外的
fromString
是因为我可能会将内部表示从String
切换到ByteString
或其他)除了能够很好地
show
它们之外,我更安全了,因为在编写代码时,我不能混合使用不同类型的单词。fnatzsnv3#
在Ghci的下一个版本7.6.1中,事情会有所改变,因为它提供了一个新的Ghci选项,名为:-interactive-print。这里是从ghc-manual复制的:(我写了myShow和myPrint如下)
字符串
他们工作得很好:
型
83qze16e4#
选项1(坏):
修改这行代码:
https://github.com/ghc/packages-base/blob/ba98712/GHC/Show.lhs#L356
字符串
并重新编译GHC。
选项2(大量工作):
当GHCi类型检查一个解析的语句时,它会在
tcRnStmt
中结束,而mkPlan
依赖于mkPlan
(两者都在https://github.com/ghc/ghc/blob/master/compiler/typecheck/TcRnDriver.lhs中)。这试图对输入的语句的几个变体进行类型检查,包括:型
具体而言:
型
这里可能需要更改的是
printName
(它绑定到System.IO.print
)。如果它绑定到类似printGhci
的东西,实现方式如下:型
然后,Ghci可以通过将不同的示例引入上下文来改变打印的内容。
btqmn9zl5#
您可以切换到使用'text'包进行IO。
字符串
这个包是标准Haskell发行版t he Haskell Platform的一部分,它提供了一个高效的打包的、不可变的Unicode文本类型和IO操作。
使用一个.ghci文件,你可以将-XOverloadStrings设置为默认打开,然后编写一个
:def
宏来引入一个:text
命令,该命令只通过text
显示一个值。8mmmxcuj6#
现在我知道ghci的
-interactive-print
,这是一个很好的功能。非常感谢你写的问题和答案!顺便说一下,现有的漂亮的打印机,我可以在网上找到have some corner cases,和写好的Unicodeshow
的问题原来比它看起来更复杂。因此,我决定为此编写一个Haskell包unicode-show,即(希望)prints cornercase strings and compound types well。
最好的祝愿,这个包是有用的人谁搜索这个Q&A:)
ozxc1zmp7#
理想的情况是ghci的补丁允许用户
:set
一个函数来显示show
以外的结果。目前还没有这样的功能存在。然而,Don对:def
宏的建议(带或不带文本包)一点也不坏。sf6xfgos8#
一个可能的好解决方案是:
1.安装
pretty-simple
,例如使用cabal
:字符串
1.添加到
~/.ghci
:型
pretty-simple
库在打印各种类型的数据时提供了额外的好处。