什么是编写`(\ x - > traceShow x x)`的简短方法?

时间:2015-10-10 20:59:02

标签: debugging haskell

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

my . super . cool . fUnCtIoN . chain $ value

我试图查看fUnCtIoN . chain的输出,我会添加

my . super . cool . (\ x -> traceShow x x ) . fUnCtIoN . chain $ value

对于像这样的简单任务而言是满口的,更不用说我是否想要打印许多中间结果:

(\ x -> traceShow x x )
    . my
    . (\ x -> traceShow x x )
    . super
    . (\ x -> traceShow x x )
    . cool
    . (\ x -> traceShow x x )
    . fUnCtIoN
    . (\ x -> traceShow x x )
    . chain
    $ value

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

4 个答案:

答案 0 :(得分:7)

只需使用traceShowId!它完全符合您的要求。

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

答案 1 :(得分:4)

是。 join traceShow

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

如果是函数monad join f x = f x x,那么join traceShow相当于\x -> traceShow x x

或者制作where子句,提供(.)的新定义:

--...your code without the nasty bits...
    where
       (.) f g a = f ( join traceShow (g a))

这可能只会有所帮助,但会有比以前更多的traceShow来电。

答案 2 :(得分:2)

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

dbg :: Show a => String -> a -> a
dbg name x = trace (name ++ ": " ++ show x) x

main = do
  let x = dbg "my"    . my
        . dbg "super" . super
        . dbg "cool"  . cool
        . dbg "func"  . fUnCtIoN
        . dbg "chain" . chain
        $ value
  print x

my       = (+1)
super    = (+2)
cool     = (+3)
fUnCtIoN = (+4)
chain    = (+5)
value = 3

输出:

chain: 3
func: 8
cool: 12
super: 15
my: 17
18

答案 3 :(得分:1)

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

applyBoth :: (a -> a -> b) -> a -> b
applyBoth f x = f x x

(旁白:对于“读者”monad join,这是(->) a。)

然后你可以以咖喱形式使用该组合器:

applyBoth traceShow
    . my
    . applyBoth traceShow
    . super
    . applyBoth traceShow
    . cool
    . applyBoth traceShow
    . fUnCtIoN
    . applyBoth traceShow
    . chain
    $ value

或者为applyBoth traceShow定义别名。

traceS = applyBoth traceShow

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

对于最大的简洁点,您可以通过折叠将traceS自动交错到函数列表中:

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

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

编辑呃,到底是什么......它并不完全相关,但是当你想通过多种类型管道数据时,如何让showSteps工作。这是我们在没有GHC的高级类型系统功能的情况下无法编写的程序示例(在此实例中为GADTsRankNTypes)。

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,那么您可以从yz采取路径,您可以从x转到zEnd适用于您到达目的地的时间 - 从z步行至z非常容易,不会走路。

所以Path具有与链表相同的递归结构,但是对其中的内容采用更灵活的方法。只要一个箭头的返回类型与下一个箭头的输入类型匹配,它就不会要求所有元素具有相同的类型,而是为您提供一种连接像多米诺骨牌一样的箭头的方法。 (若要使用数学术语:如果您将基础类别c视为逻辑关系,则End augments c with reflexivity:-> augments c with transitivityPath c因此构造自反传递闭包<{em> c。另一种看待此问题的方法是Path免费类别,就像[]免费幺半群;您可以instance Category (Path c)定义c而不受Path约束。)

您可以使用与折叠列表完全相同的代码折叠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 ,但类型更精确:折叠功能无法知道 a priori 关于路径内箭头的类型。

type TAS = Path (->)

此时,我可以定义类型对齐的函数序列(f :-> g :-> h :-> End),并向您展示h . g . f如何折叠到->,但由于我们的目标是打印除了所有中间值之外,我们必须使用比普通旧data Showoff x y where Showoff :: Show y => (x -> y) -> Showoff x y 更多结构的类别。 (感谢@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

traceS

在所有这些强烈类型的乐趣中,使用不纯的 String权利让我感到有些羞耻。在现实生活中,我可能会回答String以及答案。

为了证明它确实有效,这里有一系列不同类型的函数。我们将readInt加入Float,向其添加一个,将其转换为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 ,然后将其除以2.

$draw = new \ImagickDraw();

$draw->setFont("../fonts/Arial.ttf");
$draw->setFontSize(48);
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$draw->setFillColor('#ff0000');

$textOnly = new \Imagick();
$textOnly->newImage(600, 300, "rgb(230, 230, 230)");
$textOnly->setImageFormat('png');
$textOnly->annotateImage($draw, 30, 40, 0, 'Your Text Here');
$textOnly->trimImage(0);
$textOnly->setImagePage($textOnly->getimageWidth(), $textOnly->getimageheight(), 0, 0);

$distort = array(180);
$textOnly->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);

$textOnly->setImageMatte(true);
$textOnly->distortImage(Imagick::DISTORTION_ARC, $distort, false);

$textOnly->setformat('png');

header("Content-Type: image/png");
echo $textOnly->getimageblob();

趣!