6

假设我有一个函数(它没有任何实际应用,只是一种学术兴趣,因此用幺半群、应用函子和定点组合器编写它的方式很奇怪)

f :: Num a => a -> Sum a
f = fix ((<>) <$> Sum <*>)

它会进行类型检查,但在我测试它之前,我不能确定它是否能达到预期的效果。

如何进行测试和/或调试呢?我的意思是在几次迭代后看到结果,就像使用take 10 [1..].

ghci我对like:break和的简单调试工具了解一些:step,但它会进入非终止计算,所以我无法检查任何东西(它甚至是有问题的^C)。而且我也不知道如何在这个函数中使用tracefrom模块。Debug

任何指针将不胜感激。

4

1 回答 1

10

ChasingBottoms带有它的包approxShow可以帮助您探索部分评估的值:

$ cabal install ChasingBottoms
$ ghci
> import Test.ChasingBottoms.ApproxShow
> import Data.Function
> approxShow 10 (fix (1:))
"[1, 1, 1, 1, 1, 1, 1, 1, 1, _"

但是,这里我们不能直接使用它:对Integers 求和是严格的,不像(:)用于构建列表。因此应使用另一种类型。

首先,一些导入(我们还需要能够派生Data,以便approxShow可以用来显示我们的自定义类型):

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Monoid
import Data.Function
import Control.Applicative
import Test.ChasingBottoms.ApproxShow

类型本身(非常基本)及其Num实例:

data S = N Integer | S :+ S
  deriving (Typeable, Data)

instance Num S where
  (+) = (:+)
  fromInteger = N
  --other operations do not need to be implemented

最后,函数:

f :: S -> Sum S
f = fix ((<>) <$> Sum <*>)

下面是我们如何看到f一个常见的数字(例如 1)在做什么:

*Main> approxShow 5 (getSum (f 1))
"(N 1) :+ ((N 1) :+ ((N 1) :+ ((N _) :+ (_ :+ _))))"

当然,观察演变可能会更有趣:

*Main> Control.Monad.forM_ [0..7] $ \i -> putStrLn $ approxShow i (getSum (f 1))
_
_ :+ _
(N _) :+ (_ :+ _)
(N 1) :+ ((N _) :+ (_ :+ _))
(N 1) :+ ((N 1) :+ ((N _) :+ (_ :+ _)))
(N 1) :+ ((N 1) :+ ((N 1) :+ ((N _) :+ (_ :+ _))))
(N 1) :+ ((N 1) :+ ((N 1) :+ ((N 1) :+ ((N _) :+ (_ :+ _)))))
(N 1) :+ ((N 1) :+ ((N 1) :+ ((N 1) :+ ((N 1) :+ ((N _) :+ (_ :+ _))))))
于 2013-04-05T12:52:42.383 回答