Question

Say I have a function (it doesn't have any practical application, just an academic interest, thus weird way to write it, with monoids, applicative functors and fixpoint combinators)

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

It typechecks, but I can't be sure it does what it is expected to do before I can test it.

How would one go about testing and/or debugging it? I mean something like seeing the result after several iterations like it is possible with take 10 [1..].

I know a little about simple debugging facilities of ghci like :break and :step, but it steps into non-terminating calculation so I can't inspect anything (it's even problematic to ^C it). And I can't figure how to use trace from Debug module in this function either.

Any pointers would be appreciated.

Was it helpful?

Solution

Package ChasingBottoms with its approxShow can help you explore partially evaluated values:

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

However, here we can’t use it directly: summation over Integers is strict, unlike (:) which is used to build a list. Therefore another type should be used.

First, some imports (we also need to be able to derive Data, so that approxShow could be used to show our custom type):

{-# LANGUAGE DeriveDataTypeable #-}

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

The type itself (very basic), and its Num instance:

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

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

Finally, the function:

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

And here is how we can see what f is doing with, say, a common number such as 1:

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

Of course, it may be more interesting to watch the evolution:

*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 _) :+ (_ :+ _))))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top