I'm learning Haskell and I decided to implement this simple algorithm for doing part of the insert sort algorithm:
while j > 0 and A[j-1] > A[j]
swap A[j] and A[j-1]
j ← j - 1
end while
I did like this:
miniSort:: (Eq(a), Ord(a)) => Int -> [a] -> [a]
miniSort j list = if (list !! j) < (list !! (j-1)) && j >0
then miniSort (j-1) (swapElements j (j-1) list)
else list
It was kinda hard to get it right but I did (I guess).
While in imperative programming languages to view each step I could simply do
while j > 0 and A[j-1] > A[j]
print("j is $j")
print("swapping $A[j] with $A[j-1]")
swap A[j] and A[j-1]
print("swapped list: $A")
j ← j - 1
end while
print("ended with j $j")
On Haskell it's much much harder to insert logging functionality through the Writer Monad. I didn't even try because it would be a mess, and then when I wanted to clean the logging things it would be another mess.
Isn't there a way to view the function calls branching in Haskell?
For example:
miniSort 3 [1,2,4,3,7,8,5]
Would expand to something like this:
miniSort 3 [1,2,4,3,7,8,5] = if (3) < 4 && 3 >0
then miniSort (2) [1,2,3,4,7,8,5]
miniSort 2 [1,2,3,4,7,8,5] = if (3) < 2 && 2 >0
else [1,2,3,4,7,8,5]
[1,2,3,4,7,8,5]
As Fyodor Soikin said, trace inserts debug messages into arbitrary code, which will be printed when the statement you apply it to is evaluated.
import Debug.Trace
import Text.Printf
miniSort j list
= if trace (printf "miniSort %i %s = if %i < %i && %i>0"
j (show list) (list!!j) (list!!(j-1)) j)
$ list !! j < list !! (j-1) && j>0
then trace (printf "then miniSort %i %s" (j-1) (show list))
miniSort (j-1) (swapElements j (j-1) list)
else traceShowId list
However, a couple of caveats:
If you find yourself wanting this for a simple algorithm like a sort, you're doing something wrong. Which indeed you are – it makes no sense to sort a list like that in Haskell. Specifically, indexing into lists is almost never a good idea: it's awkward, error-prone and slow.
Good Haskell code generally uses pattern matching instead of indexing etc.. For example,
type SortedList a = [a]
insertion :: Ord a => a -> SortedList a -> SortedList a
insertion n (x:xs)
| n>x = x : insertion n xs
insertion n xs = n : xs
insertSort :: Ord a => [a] -> SortedList a
insertSort [] = []
insertSort (x:xs) = insertion x $ insertSort xs
See, no indices anywhere. Much less that can go wrong here.
(Whether it makes any sense to implement insertion sort on Haskell lists in the first place is of course another matter!)
Because (non-monadic) Haskell doesn't really specify any evaluation order, trace can often come out in unexpected, possibly jumbled order. Really, only use it for debugging a single detail in the middle of a big, already-existing function. In general it's much better to instead refactor and unit-test thoroughly.
And never use trace for actual logging!
Using the writer monad not only solves these problems, it also makes it easier (and much more reliable) to remove such statements again. For one thing, you don't really need to do that at all because you can just ignore the log data, use a polymorphic monad with dummy logging that will optimise it away from being generated in the first place, etc.. If you do remove the statements and un-writer the type, then the type checker will highlight any places where you forgot to do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With