我希望能够在ghci中输入以下内容:
map showTypeSignature [(+),(-),show]
我希望ghci返回以下字符串列表:
["(+) :: Num a => a -> a -> a","(-) :: Num a => a -> a -> a","show :: Show a => a -> String"]
当然,我遇到麻烦的第一个地方是我无法构造第一个列表,因为函数的类型签名不匹配。我该怎么做才能构建这样一个列表? ghci如何完成类型签名的打印? ghci命令在哪里定义(其来源)?
答案 0 :(得分:7)
你要求的东西真的不可能。您无法从Haskell中轻松确定Haskell术语的类型签名。在运行时,几乎没有可用的类型信息。由于某种原因,GHCi命令:t
是GHCi命令,而不是解释的Haskell函数。
要做一些接近你想要的事情,你必须使用GHC本身作为一个库。 GHC为此目的提供GHC API。但是,您将无法使用任意Haskell术语,但必须以String
表示您的术语开头。此外,在运行时调用编译器必然会产生IO
输出。
答案 1 :(得分:6)
kosmikus是对的,这并没有真正奏效。并且不应该,静态类型系统是Haskell最具特色的功能之一!
但是,您可以使用Dynamic
existential很好地模拟单态函数:
showTypeSignature :: Dynamic -> String
showTypeSignature = show . dynTypeRep
Prelude Data.Dynamic>
map showTypeSignature [toDyn (+), toDyn (-), toDyn (show)]
["整数 - >整数 - >整数","整数 - >整数 - >整数","() - > [字符]"]
正如你所看到的,ghci必须将这些函数简化为单态类型才能使其正常工作,尤其对于show
来说,这显然是无用的。
答案 2 :(得分:2)
关于你为什么不能这样做的答案是非常好的,但可能还有另一种选择。如果您不关心获取Haskell列表,并且只想查看一堆内容的类型,您可以define a custom GHCi command,比如说:ts
,它会显示一系列事物的类型;好的,
Prelude> :ts (+) (-) show
(+) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> a
show :: Show a => a -> String
为此,我们使用:def
; :def NAME EXPR
,其中NAME
是标识符,EXPR
是类型String -> IO String
的Haskell表达式,定义GHCi命令:NAME
。运行:NAME ARGS
计算EXPR ARGS
以生成字符串,然后在GHCi中运行生成的字符串。这听起来不那么令人困惑。这就是我们的工作:
Prelude> :def ts return . unlines . map (":type " ++) . words
Prelude> :ts (+) (-) show
(+) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> a
show :: Show a => a -> String
发生了什么事?这定义:ts
来评估return . unlines . map (":t " ++) . words
,其执行以下操作:
words
:获取一个字符串并将其拆分为空格; 例如,"(+) (-) show"
变为["(+)", "(-)", "show"]
。map (":type " ++)
:取自之前的每个单词并在":type "
之前加上; 例如,["(+)", "(-)", "show"]
变为[":type (+)", ":type (-)", ":type show"]
。请注意,我们现在有一个GHCi命令列表。unlines
:获取字符串列表并在每个字符串后面添加换行符; 例如,[":type (+)", ":type (-)", ":type show"]
变为":type (+)\n:type (-)\n:type show\n"
。请注意,如果我们将此字符串粘贴到GHCi中,它将生成我们想要的类型签名。return
:将String
提升为IO String
,因为这是我们需要的类型。因此,:ts name₁ name₂ ... nameₙ
将连续评估:type name₁
,:type name₂
,...,:type nameₙ
并打印出结果。同样,你不能以这种方式得到一个真正的列表,但如果你只是想看看类型,这将有效。