Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging infinite Sum in Haskell

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.

like image 986
dmedvinsky Avatar asked Apr 05 '13 11:04

dmedvinsky


1 Answers

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 _) :+ (_ :+ _))))))
like image 172
Artyom Avatar answered Sep 28 '22 11:09

Artyom