如何破解GHCi(或Hugs),使其打印Unicode字符而不被替换?

如何破解GHCi(或Hugs),使其打印Unicode字符而不被替换?,unicode,haskell,formatting,locale,ghci,Unicode,Haskell,Formatting,Locale,Ghci,看看问题:通常情况下,在交互式Haskell环境中,非拉丁Unicode字符(构成结果的一部分)被转义打印,即使区域设置允许这样的字符(与通过putStrLn,putChar直接输出相反,后者看起来很好且可读)--示例显示了GHCi和Hugs98: $ ghci GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help Prelude> "hello: привет" "hello: \1087\1088\1080\1074

看看问题:通常情况下,在交互式Haskell环境中,非拉丁Unicode字符(构成结果的一部分)被转义打印,即使区域设置允许这样的字符(与通过
putStrLn
putChar
直接输出相反,后者看起来很好且可读)--示例显示了GHCi和Hugs98:

$ 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=
$ 
我们可以猜测,这是因为and用于格式化结果,而这些函数尽其所能以规范的、最大限度可移植的方式格式化数据——因此它们更喜欢避开奇怪的字符(也许,Haskell的标准中甚至规定了这一点):

但是,如果我们知道如何破解GHCi或Hugs,以非常人性化的可读方式打印这些字符,也就是说,直接,不加修饰,那就太好了。在教育目的中使用交互式Haskell环境时,如果您想在非英语观众面前展示一些Haskell的人类语言数据,您可以欣赏这一点

实际上,它不仅对教育目的有用,而且对调试也很有用!如果函数是在表示其他语言的单词的字符串上定义的,且带有非ASCII字符。因此,如果程序是特定于语言的,并且只有另一种语言的单词才有意义作为数据,并且您的函数只在这些单词上定义,那么在GHCi中调试时查看这些数据是很重要的

总结一下我的问题:有什么方法可以破解现有的交互式Haskell环境,以便在结果中更友好地打印Unicode?(“Friendlier”在我的例子中意味着更“简单”:我想用GHCi或Hugs中的
print
,以简单直接的方式显示非拉丁字符,如
putChar
putStrLn
,即unscaped。)


(也许,除了GHCi和Hugs98之外,我还将研究一下与Haskell交互的现有Emacs模式,看看它们是否能够以漂亮的、不带scaped的方式呈现结果。)

解决这一问题的一种方法是将GHCi包装到一个shell包装器中,该包装器可以读取其stdout和unescapes Unicode字符。当然,这不是哈斯克尔的方法,但它确实起到了作用:)

例如,这是一个使用
sh
python3
的包装器
ghci-esc
(这里3很重要):

ghci esc的使用

$ ./ghci-esc
GHCi, version 7.0.2: http://www.haskell.org/ghc/  :? for help
> "hello"
"hello"
> "привет"
"привет"
> 'Я'
'Я'
> show 'Я'
"'\Я'"
> :q
Leaving GHCi.

请注意,并非以上所有的取消跳过操作都正确完成,但这是一种向观众显示Unicode输出的快速方法。

您可以切换到使用IO软件包。例如

Prelude> :set -XOverloadedStrings
Prelude> Data.Text.IO.putStrLn "hello: привет"
hello: привет
该包是标准Haskell发行版t的一部分,它提供了一种高效的打包的、不可变的Unicode文本类型以及IO操作。很多

使用.ghci文件,您可以在默认情况下将-XOverloadStrings设置为打开,并编写一个
:def
宏来引入一个
:text
命令,该命令仅通过
text
显示值。那就行了。

选项1(坏): 修改这行代码:

并重新编译ghc

备选方案2(大量工作): 当GHCi类型检查已解析的语句时,它会在依赖于
mkPlan
(两者都在)的
tcRnStmt
中结束。这将尝试对键入的语句的多个变体进行类型检查,包括:

let it = expr in print it >> return [coerce HVal it]
具体而言:

print_it  = L loc $ ExprStmt (nlHsApp (nlHsVar printName) (nlHsVar fresh_it))
                                      (HsVar thenIOName) placeHolderType
这里可能需要更改的是
printName
(它绑定到
System.IO.print
)。如果它改为绑定到类似于
printGhci
的东西,其实现方式如下:

class ShowGhci a where
    showGhci :: a -> String
    ...

-- Bunch of instances?

instance ShowGhci Char where
    ...  -- The instance we want to be different.

printGhci :: ShowGhci a => a -> IO ()
printGhci = putStrLn . showGhci

然后,Ghci可以通过将不同的实例放入上下文中来更改打印内容。

理想的做法是为Ghci提供一个补丁,允许用户
:设置一个用于显示结果而不是
显示
的函数。目前不存在此类功能。但是,Don建议使用
:def
宏(带或不带文本包)一点也不坏

在这个问题上取得了一些进展;感谢布拉维特(维塔利·布拉吉列夫斯基)!:

  • 在建工程:——围绕相关工程
  • 作品的结果是:与布拉维特(维塔利·布拉吉列夫斯基饰)合著,另一部由布拉维特(维塔利·布拉吉列夫斯基饰)合著。这些增强已承诺:和
可能纳入GHC 7.6.1。(是吗?)

:

传递给GHCi的参数应该是可以打印西里尔文的函数。在Hackage上没有发现这样的功能。因此,我们必须创建一个简单的包装器,目前:

module UPPrinter where
import System.IO
import Text.PrettyPrint.Leijen

upprint a = (hPutDoc stdout . pretty) a >> putStrLn ""
然后以这种方式运行
ghci
ghci-interactive print=UPPrinter.upprint UPPrinter

当然,这可以一劳永逸地写进
.ghci

实际问题:想出另一个nice
Show
因此,现在有一个实际的问题:用什么来代替标准的
Show
(标准的
Show
——违背我们的意愿逃逸想要的符号)

使用他人作品:其他漂亮的打印机 上面建议使用
Text.PrettyPrint.Leijen
,这可能是因为已知它不能在字符串中转义这些符号

我们自己的表演是基于表演的——有吸引力,但不实用 按照这里的回答,写我们自己的
Show
,比如说,
ShowGhci
,怎么样。实用吗

要保存为备用
Show
类(如
ShowGhci
)定义实例的工作,可能会尝试使用
Show
的现有实例。默认情况下,只需为
String
Char
重新定义实例。但这是行不通的,因为如果使用
showGhci=show
,那么对于任何包含字符串的复杂数据,
show
都是“硬编译”的,可以调用old
show
来显示字符串。这种情况要求能够将实现同一类接口的不同词典传递给使用该接口的函数(
show
将其传递给sub
print_it  = L loc $ ExprStmt (nlHsApp (nlHsVar printName) (nlHsVar fresh_it))
                                      (HsVar thenIOName) placeHolderType
class ShowGhci a where
    showGhci :: a -> String
    ...

-- Bunch of instances?

instance ShowGhci Char where
    ...  -- The instance we want to be different.

printGhci :: ShowGhci a => a -> IO ()
printGhci = putStrLn . showGhci
module UPPrinter where
import System.IO
import Text.PrettyPrint.Leijen

upprint a = (hPutDoc stdout . pretty) a >> putStrLn ""
newtype Vocable = Vocable2 { ortho :: String } deriving (Eq,Ord)
instance IsString Vocable -- to simplify typing the values (with OverloadedStrings)
    where fromString = Vocable2 . fromString
newtype Lexeme = Lexeme2 { lemma :: String } deriving (Eq,Ord)
instance IsString Lexeme -- to simplify typing the values (with OverloadedStrings)
    where fromString = Lexeme2 . fromString
2.4.8. Using a custom interactive printing function

[New in version 7.6.1] By default, GHCi prints the result of expressions typed at the prompt using the function System.IO.print. Its type signature is Show a => a -> IO (), and it works by converting the value to String using show.

This is not ideal in certain cases, like when the output is long, or contains strings with non-ascii characters.

The -interactive-print flag allows to specify any function of type C a => a -> IO (), for some constraint C, as the function for printing evaluated expressions. The function can reside in any loaded module or any registered package.

As an example, suppose we have following special printing module:

     module SpecPrinter where
     import System.IO

     sprint a = putStrLn $ show a ++ "!"

The sprint function adds an exclamation mark at the end of any printed value. Running GHCi with the command:

     ghci -interactive-print=SpecPrinter.sprinter SpecPrinter

will start an interactive session where values with be printed using sprint:

     *SpecPrinter> [1,2,3]
     [1,2,3]!
     *SpecPrinter> 42
     42!

A custom pretty printing function can be used, for example, to format tree-like and nested structures in a more readable way.

The -interactive-print flag can also be used when running GHC in -e mode:

     % ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter
     [1,2,3]!


module MyPrint (myPrint, myShow) where
-- preparing for the 7.6.1
myPrint :: Show a => a -> IO ()
myPrint = putStrLn . myShow

myShow :: Show a => a -> String
myShow x = con (show x) where
  con :: String -> String
  con [] = []
  con li@(x:xs) | x == '\"' = '\"':str++"\""++(con rest)
                | x == '\'' = '\'':char:'\'':(con rest')
                | otherwise = x:con xs where
                  (str,rest):_ = reads li
                  (char,rest'):_ = reads li
*MyPrint> myPrint "asf萨芬速读法"
"asf萨芬速读法"
*MyPrint> myPrint "asdffasdfd"
"asdffasdfd"
*MyPrint> myPrint "asdffa撒旦发"
"asdffa撒旦发"
*MyPrint> myPrint '此'
'此'
*MyPrint> myShow '此'
"'\27492'"
*MyPrint> myPrint '此'
'此'