简体   繁体   English

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

[英]What's a shorter way of writing `(\ x -> traceShow x x )`?

Printing the value of an expression is a common practice in debugging. 打印表达式的值是调试中的常见做法。 For example, if I have a piece of code like this 例如,如果我有一段这样的代码

my . super . cool . fUnCtIoN . chain $ value

and I am trying to see the output of fUnCtIoN . chain 我试图看到fUnCtIoN . chain的输出fUnCtIoN . chain fUnCtIoN . chain , I would add fUnCtIoN . chain ,我会补充

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

which is mouthful for a simple task like this, not to mention if I want to print many intermediate results: 对于像这样的简单任务来说,这是满口的,更不用说我是否要打印许多中间结果:

(\ 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

It would just look awful. 它看起来很糟糕。 Is there a better way to do this? 有一个更好的方法吗?

Just use traceShowId ! 只需使用traceShowId It does exactly what you're asking for. 它完全符合您的要求。

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

Yes. 是。 join traceShow . join traceShow

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

In the case of the function monad, join fx = fxx , so join traceShow is equivalent to \\x -> traceShow xx . 在函数monad的情况下, join fx = fxx ,因此join traceShow等效于\\x -> traceShow xx

Or make a where clause that provides a new definition of (.) : 或者创建一个提供(.)新定义的where子句:

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

Which may just help, though there will be one more traceShow call than previously. 这可能只是帮助,虽然会有比以前更多的traceShow调用。

How about a helper function for adding a trace call to a function: 如何为函数添加跟踪调用的辅助函数:

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

Output: 输出:

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

You could write a higher-order function which takes a function of two arguments and uses the same value for both arguments. 你可以编写一个高阶函数,它接受两个参数的函数,并对两个参数使用相同的值。

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

(Aside: this is join for the "reader" monad (->) a .) (旁白:这是join “读者”monad (->) a 。)

Then you can use that combinator in curried form: 然后你可以以咖喱形式使用该组合器:

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

Or define an alias for applyBoth traceShow . 或者为applyBoth traceShow定义别名。

traceS = applyBoth traceShow

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

For maximum terseness points, you can automatically interleave traceS into a list of functions by folding it up: 对于最大的简洁点,您可以通过折叠将traceS自动交错到函数列表中:

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

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

Edit Eh, what the hell... It's not entirely relevant, but here's how to make showSteps work when you want to pipeline your data through a number of types. 编辑呃,到底是什么......这并不完全相关,但是当你想通过多种类型管道数据时,如何使showSteps工作。 It's an example of a program we wouldn't be able to write without GHC's advanced type system features ( GADTs and RankNTypes in this instance). 这是我们在没有GHC的高级类型系统功能(本例中为GADTsRankNTypes )的情况下无法编写的程序示例。

Path is a GADT which explains how to walk through a directed graph of types, starting at the source type x and ending at the destination type y . Path是一个GADT,它解释了如何遍历类型的有向图,从源类型x开始到结束目标类型y It's parameterised by a category c :: * -> * -> * . 它由类别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

:-> reminds us that a journey of a thousand miles begins with a single step: if the category you're working in lets you go from x to y , and you can take a path from y to z , you can go from x to z . :->提醒我们,一千英里的旅程从一步开始:如果您正在使用的类别允许您从xy ,并且您可以从yz的路径,您可以从xz End is for when you have reached your destination - it's pretty easy to walk from z to z by not walking at all. End是当你已经达到你的目的地-它很容易从步行zz通过不走的。

So Path has the same recursive structure as a linked list, but with a more flexible approach to the things inside it. 因此Path具有与链表相同的递归结构,但对其内部的事物采用更灵活的方法。 Rather than requiring all of its elements to have the same type, it gives you a way to join up arrows like dominos, as long as the return type of one arrow matches the input type of the next. 只要一个箭头的返回类型与下一个箭头的输入类型匹配,它就不会要求所有元素具有相同的类型,而是为您提供一种连接像多米诺骨牌一样的箭头的方法。 (To use the mathematical jargon: if you view the underlying category c as a logical relation, then End augments c with reflexivity and :-> augments c with transitivity . Path c thus constructs the reflexive transitive closure of c . Another way of looking at this is that Path is the free category , much like [] is the free monoid ; you can define instance Category (Path c) without any constraint on c .) (要使用的数学术语:如果查看下层类别c作为逻辑关系,然后End增强c与自反:->增强c与传递性Path c由此构建的自反传递闭包 c看待的另一种方式。这就是Path自由类 ,很像[]自由的monoid ;你可以定义instance Category (Path c)而不对c任何约束。)

You can fold up a Path with exactly the same code as you use to fold up a list, but the type is more precise: the folding function can't know anything a priori about the types of the arrows inside the path. 您可以使用与用于折叠列表完全相同的代码折叠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

At this point, I could define type-aligned sequences of functions ( type TAS = Path (->) ) and show you how f :-> g :-> h :-> End can be folded up into h . g . f 此时,我可以定义类型对齐的函数序列( type TAS = Path (->) )并向您展示f :-> g :-> h :-> End可以折叠成h . g . f h . g . f h . g . f , but since our goal is to print out all the intermediate values, we have to use a category with a tiny bit more structure than plain old -> . h . g . f ,但由于我们的目标是打印出所有中间值,我们必须使用比普通旧的结构稍微多一点的类别-> (Thanks to @dfeuer in the comments for the suggestion - I've adjusted the name he gave to better reflect the attention-seeking nature of my behaviour.) (感谢@dfeuer在建议的评论中 - 我调整了他给出的名字,以更好地反映我的行为引起注意的性质。)

data Showoff x y where
    Showoff :: Show y => (x -> y) -> Showoff x y

Showoff is just like a regular function, except it assures you that the return value y will be Show able. Showoff就像一个常规函数,除了它确保返回值y将是Show able。 We can use this extra bit of knowledge to write showSteps for paths in which each step is a Showoff . 我们可以使用这些额外的知识为每个步骤为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

It strikes me as a bit of a shame to use the impure traceS right in the midst of all this strongly typed fun. 在所有这种强烈类型的乐趣中使用不纯净的 traceS这让我感到有点遗憾。 In real life I'd probably return a String along with the answer. 在现实生活中,我可能会回答一个String以及答案。

To prove that it does actually work, here is a chain of functions with varying types. 为了证明它确实有效,这里有一系列不同类型的函数。 We take in a String , read it into an Int , add one to it, convert it to a Float , then divide it by 2. 我们接受一个StringreadreadInt ,向其中添加一个,将其转换为Float ,然后将其除以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

Fun! 有趣!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM