Gödel’s Beta-function in Haskell
In my previous post on the β function, I mentioned that I would like to rewrite it using Java's BigInteger class in order to escape the overflow errors that arise on almost any list with more than 3 elements. Although I never did this (I did mess around with redoing the OCaml implementation using their big_int data type), I decided to redo the implementation in Haskell. Not only have I wanted to learn Haskell for assorted reasons, but I realized that it has an Integer data type which allows arbitrary precision integers. So now I could kill two birds (learning Haskell and implementing the beta function with arbitrary precision integers) with one stone.
Below you will find the code of the Haskell implementation. I will follow the code with some comments about it, including the features of Haskell that this project helped me uncover and that I like very much. Note, however, that this post is NOT a general introduction to Haskell or a general comparison of Haskell with any other language. Links to such resources will be provided at the end of the post. First, note that the Haskell syntax for type declarations is e::t instead of OCaml's e:t. (In Haskell, ':' is the list cons operation.) I have also set up a git repository on GitHub containing all of the implementations. The Haskell one can be found there as well.
Although I mentioned that 'e::t' are typing declarations, you may have noticed the notation 'beta::Integral a => [a] -> a -> a'. In this case, 'a' is a type variable. Thus the expression 'beta' has a polymorphic type, similar to 'a in OCaml. Now, everything before the '=>' is a type constraint, that is, constraints that the type variables must satisfy. There can be multiple such constraints even though all the examples in this case use only one.
What does the constraint 'Integral a' mean though? Integral is a type class in Haskell. A type class is akin to a Java interface in that it provides a list of functions (and their types) that must be applicable to a type in order for it to be an instance of the class. The Integral type class provides methods for integer division, modulus, and the like. It inherits from the Num type class (Integral is actually a subclass of Real, not of Num, but that's irrelevant for our purposes) methods for addition, subtraction, and multiplication. Now, particular types are instances of a type class. The only types that are instances of Integral are Int and Integer. Int is akin to standard integer classes, while Integer (as mentioned at the outset) allows for arbitrary precision integers. For more on Haskell numbers, see this page.
Thus, in the case of 'beta::Integral a => [a] -> a-> a', this type says that beta is of type [a] -> a -> a ([a] is akin to 'a list in OCaml) subject to the constraint that 'a' must be an instance of the type class Integral (i.e. must be an Int or Integer). The beautiful thing is that integer constants in Haskell can be either Int or Integer; it will decide when it needs to use arbitrary precision and when not. (Note: I do not know nearly enough about the inner workings of Haskell to know how it does so.) So, in the file above, the factorial function can be called both on small numbers like 3 and 5, and as 'factorial 3813474071308470184' on numbers that are outside the range of Int.
A few other oddities that should be pointed out:
- The `mod` and `div` refer to the modulus and division functions of the Integral type class. Surrounding any Haskell function by backticks allows it to be used in infix notation. So 'mod 2 3' is equivalent to '2 `mod` 3' although the latter is more natural to read.
- The case pattern matching at the very end is necessary because the find function in Data.List has a return type of 'Maybe a'. Thus, if the function finds no element satisfying the given predicate, it will come back with 'Nothing'. Note that in such a case, OCaml would raise an exception.
- 'listeq' is a helper function that tests two lists for equality. It also requires that the lists have the same elements in the same order; this requirement is actually critical for the β function to work as intended.
- '\x -> f' is Haskell's notation for lambda abstraction, i.e. for anonymous function declaration. While I could have inlined the functions 'remlist' and 'beta_godel', I left them separately in order to resemble my previous implementations. I left them separately in those implementations to remain similar to Enderton's exposition.
- '[0 ... n]' is a very nice way to denote the list of numbers from 0 to n. Although I didn't use it in this implementation, Haskell has very nice list comprehensions.
To do
Although this implementation does work (I will post examples shortly), more remains to be done.
- I still need to investigate
. Two things directly affect the size of this number:
and
.
- Haskell has a type inference engine so that the type declarations in this file are not necessary (although I do find them useful). I'm curious to see whether Haskell will infer more general types than I've given the functions.
- I need to add more detailed comments to the source files and clean up the git repository.
- In due time, I will do real speed testing of the three implementations.
Resources
I refer the reader to my old post, linked at the beginning, for references about the logical details.
- Learn You a Haskell for Great Good! This is a great introductory book that can be read for free online.
- Haskell Wikibook
- Some notes on OCaml vs. Haskell.

![Reblog this post [with Zemanta]](http://img.zemanta.com/reblog_e.png?x-id=60279121-dce2-4274-9839-b526470ac869)