Debugging 什么';写“(\x->;traceShow x)”的方式更短吗?

Debugging 什么';写“(\x->;traceShow x)”的方式更短吗?,debugging,haskell,Debugging,Haskell,打印表达式的值是调试中的常见做法。例如,如果我有一段这样的代码 my。超级的。酷。功能。价值链 我试图看到函数的输出。链,我想补充一下 my。超级的。酷。(\x->traceShow x)。功能。价值链 这对于这样一个简单的任务来说是非常有用的,更不用说如果我想打印许多中间结果: (\x->traceShow x) . 我的 . (\x->traceShow x) . 超级的 . (\x->traceShow x) . 凉爽的 . (\x->traceShow x) . 作用 . (\x->

打印表达式的值是调试中的常见做法。例如,如果我有一段这样的代码

my。超级的。酷。功能。价值链
我试图看到
函数的输出。链
,我想补充一下

my。超级的。酷。(\x->traceShow x)。功能。价值链
这对于这样一个简单的任务来说是非常有用的,更不用说如果我想打印许多中间结果:

(\x->traceShow x)
. 我的
. (\x->traceShow x)
. 超级的
. (\x->traceShow x)
. 凉爽的
. (\x->traceShow x)
. 作用
. (\x->traceShow x)
. 链条
美元价值

这看起来很糟糕。有更好的方法吗?

有<代码>加入traceShow

λ>  import Control.Monad
λ> :t join
join :: Monad m => m (m a) -> m a
λ> :t join (+)
join (+) :: Num a => a -> a
traceS = applyBoth traceShow

traceS
    . my
    . traceS
    . super
    . traceS
    . cool
    . traceS
    . fUnCtIoN
    . traceS
    . chain
    $ value
在函数monad的情况下,
join fx=fx
,因此
join traceShow
相当于
\x->traceShow x

或者创建一个
where
子句,提供
(.)的新定义


这可能会有所帮助,但会比以前多调用一个
traceShow

您可以编写一个高阶函数,它接受两个参数的函数,并对两个参数使用相同的值

applyBoth :: (a -> a -> b) -> a -> b
applyBoth f x = f x x
(旁白:这是“读卡器”monad
(>)a
)的
连接

然后,您可以使用咖喱形式的组合符:

applyBoth traceShow
    . my
    . applyBoth traceShow
    . super
    . applyBoth traceShow
    . cool
    . applyBoth traceShow
    . fUnCtIoN
    . applyBoth traceShow
    . chain
    $ value
或者为
应用程序和traceShow
定义别名

λ>  import Control.Monad
λ> :t join
join :: Monad m => m (m a) -> m a
λ> :t join (+)
join (+) :: Num a => a -> a
traceS = applyBoth traceShow

traceS
    . my
    . traceS
    . super
    . traceS
    . cool
    . traceS
    . fUnCtIoN
    . traceS
    . chain
    $ value
为了获得最大的简洁性,您可以通过折叠将
跟踪
自动交错到函数列表中:

showSteps :: Show a => [a -> a] -> a -> a
showSteps = foldr (\f g -> f . traceS . g) id

showSteps [my, super, cool, fUnCtIoN, chain] value

编辑呃,见鬼。。。这并不完全相关,但下面介绍了当您希望通过多种类型的管道传输数据时,如何使
showSteps
起作用。如果没有GHC的高级类型系统功能(
GADTs
RankNTypes
,在本例中),我们将无法编写该程序

Path
是一个GADT,它解释了如何遍历类型的有向图,从源类型
x
开始,到目标类型
y
。它由类别
c::*->*->*->*
参数化

infixr 6 :->
data Path c x y where
    End :: Path c z z
    (:->) :: c x y -> Path c y z -> Path c x z
:->
提醒我们,千里之旅始于一步:如果你工作的类别允许你从
x
y
,你可以从
y
z
,你可以从
x
z
End
适用于您到达目的地的时间-从
z
步行到
z
非常简单,根本不需要步行

因此,
Path
具有与链表相同的递归结构,但对链表中的内容采用了更灵活的方法。它不要求所有元素都具有相同的类型,而是提供了一种像多米诺骨牌一样连接箭头的方法,只要一个箭头的返回类型与下一个箭头的输入类型匹配。(使用数学术语:如果你将基本类别
c
视为一种逻辑关系,那么and.
Path c
就构成了
c
的自反传递闭包。另一种看待这一点的方式是
Path
是自由类别,很像
[]
是自由幺半群;您可以定义
实例类别(路径c)
而不受
c
的任何约束

您可以使用与折叠列表完全相同的代码折叠
路径
,但类型更精确:折叠函数无法预先知道路径中箭头的类型

foldr :: (forall x y. c x y -> r y z -> r x z) -> r z z -> Path c x z -> r x z
foldr f z End = z
foldr f z (x :-> xs) = f x $ foldr f z xs
此时,我可以定义与类型对齐的函数序列(
typetas=Path(->)
),并向您展示如何将
f:->g:->h:->End
折叠成
h。Gf
,但由于我们的目标是打印出所有中间值,因此我们必须使用比普通旧的
->
结构稍多的类别。(感谢@dfeuer在对建议的评论中——我已经调整了他给我的名字,以更好地反映我行为的注意力寻求本质。)

Showoff
与常规函数类似,只是它可以确保返回值
y
可以
Show
显示。我们可以利用这一额外的知识编写
showSteps
,用于每个步骤都是
Showoff
的路径

type ShowTAS = Path Showoff

showSteps :: ShowTAS a b -> a -> b
showSteps path = foldr combine id path . traceS
    where combine (Showoff f) g = g . traceS . f
在所有这些强类型的乐趣中使用不纯的
痕迹
,这让我感到有点羞愧。在现实生活中,我可能会随答案返回一个
字符串

为了证明它确实有效,这里有一系列不同类型的函数。我们把一个
字符串
读入一个
整数
,加上一个,把它转换成一个
浮点数
,然后除以2

chain :: ShowTAS String Float
chain = Showoff read :-> plusOne :-> toFloat :-> divideTwo :-> End

    where plusOne :: Showoff Int Int
          plusOne = Showoff (+1)

          toFloat :: Showoff Int Float
          toFloat = Showoff fromIntegral

          divideTwo :: Showoff Float Float
          divideTwo = Showoff (/2)

ghci> showSteps chain "4"
"4"
4
5
5.0
2.5
2.5  -- this last one is not from a traceShow call, it's just ghci printing the result
有趣

就用吧!它完全符合你的要求

my . super . cool . traceShowId . fUnCtIoN . chain $ value

为函数添加跟踪调用的助手函数如何:

dbg::Show a=>String->a->a
dbg名称x=跟踪(名称++”:“++show x)x
main=do
设x=dbg“my”。我的
. dbg“超级”。超级的
. dbg“酷”。凉爽的
. dbg“func”。作用
. dbg“链”。链条
美元价值
打印x
my=(+1)
超级=(+2)
酷=(+3)
函数=(+4)
链=(+5)
值=3
输出:

链:3
func:8
酷:12
超级:15
我的:17
18

这是一种有趣的方法。我不知道我可以这样使用
加入
。@trVoldemort是的。令人惊讶的是,我们经常忘记函数确实是一个单子。什么,你不打算从自同态列表中归纳到类型对齐的同态序列吗???@dfeuer我几乎做到了-我有一个名为
Path
的GADT随时准备好了-但后来我意识到这是一个过度的ans