Writing Haskell with Chat GPT
In our last couple of articles, we've asked Chat GPT a bunch of questions about Haskell and functional programming. Today we're going to do one more exploration to see what kinds of Haskell code this chatbot can write.
Hello World
We'll start with a simple "Hello World" program.
How do I write a "Hello World" program in Haskell?
Chat GPT:
To write a "Hello World" program in Haskell, you can use the putStrLn
function from the Prelude, which is Haskell's default library. This function takes a string as an argument and prints it to the console. Here's an example of a simple "Hello World" program in Haskell:
main :: IO ()
main = putStrLn "Hello, World!"
This program defines a function main that has the type IO ()
. The IO
type is used for actions that perform input/output, and the ()
type represents the empty tuple, which has no contents. The main
function is special in Haskell because it is the entry point of every executable Haskell program. When the program is run, the main
function will be called and the string "Hello, World!" will be printed to the console.
You can compile and run this program using the ghc
compiler, which is included with the Haskell Platform. First, save the program to a file named HelloWorld.hs
. Then, open a terminal and navigate to the directory where you saved the file. To compile the program, type the following command:
ghc HelloWorld.hs
This will produce an executable file named HelloWorld
. To run the program, type the following command:
./HelloWorld
This will print "Hello, World!" to the console.
Commentary
This not only gives the code to run the program, it also gives detailed instructions for running it using GHC. Recommending Stack and GHCup would be better advice for beginners though. I'm sure it would know how to describe using these tools if asked about them. But it would be best if it knew to recommend these tools to someone who didn't know about them. Still, I'm nit-picking a bit here.
Sorting Algorithms
So a "Hello World" program is one of the simplest things you can do though. How does ChatGPT do with writing some algorithmic code? Let's start with a couple simple sorting algorithms, quicksort and mergesort.
How can I write quicksort in Haskell?
Chat GPT:
Here is an implementation of quicksort in Haskell:
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = quicksort [y | y <- xs, y <= x] ++ [x] ++ quicksort [y | y <- xs, y > x]
This function defines a quicksort
that sorts a list of elements that are part of the Ord
type class (which includes types like Int
, Char
, and Double
). The function uses a recursive approach to sort the list.
The base case of the recursion is the empty list, which is already sorted. For the recursive case, the function divides the list into three parts:
- A sublist of elements that are less than or equal to the pivot element (x)
- The pivot element itself
- A sublist of elements that are greater than the pivot element
The function then recursively sorts the first and third sublists, and concatenates the sorted sublists with the pivot element to produce the final sorted list.
You can test this implementation by calling quicksort
with a list of elements:
quicksort [4, 6, 9, 8, 3, 5, 1, 7, 2]
This should return the sorted list [1, 2, 3, 4, 5, 6, 7, 8, 9]
.
How can I write mergesort in Haskell?
Chat GPT:
Here is an implementation of mergesort in Haskell:
merge :: Ord a => [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys)
| x <= y = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
mergesort :: Ord a => [a] -> [a]
mergesort [] = []
mergesort [x] = [x]
mergesort xs = merge (mergesort left) (mergesort right)
where
mid = length xs `div` 2
left = take mid xs
right = drop mid xs
This implementation defines a merge
function that merges two sorted lists into a single sorted list, and a mergesort
function that sorts a list using the merge sort algorithm.
The merge
function works by comparing the head elements of the two input lists, and adding the smaller of the two to the result list. It repeats this process until one of the input lists is empty, at which point it appends the remaining elements of the other list to the result.
The mergesort
function works by dividing the input list into two halves, sorting each half using a recursive call to mergesort
, and then merging the two sorted halves using the merge
function.
You can test this implementation by calling mergesort
with a list of elements:
mergesort [4, 6, 9, 8, 3, 5, 1, 7, 2]
This should return the sorted list [1, 2, 3, 4, 5, 6, 7, 8, 9]
.
Commentary
For quicksort, it comes up with a simple 3-line solution, and the mergesort solution is also quite succinct. However, one change I would make to the solution is to tail recursion. As with the "Hello, World" example though, the descriptions are very helpful for someone just learning the language.
Heap Sort
Quicksort and mergesort are one thing. These algorithms have fairly straightforward recursive implementations that work well in Haskell. What about heapsort?
How can I write heapsort in Haskell?
Chat GPT:
Here is an implementation of heapsort in Haskell:
heapify :: Ord a => [a] -> [a]
heapify xs = heapify' (length xs `div` 2 - 1) xs
where
heapify' (-1) xs = xs
heapify' i xs = heapify' (i - 1) (heapifyOne i xs)
heapifyOne :: Ord a => Int -> [a] -> [a]
heapifyOne i xs = case cmp of
GT -> xs
_ -> heapifyOne' i cmp xs
where
cmp = compare (xs !! i) (xs !! child)
child = if r < length xs && xs !! r > xs !! l then r else l
l = 2 * i + 1
r = 2 * i + 2
heapifyOne' i GT xs = xs
heapifyOne' i _ xs = heapifyOne (if r < length xs && xs !! r > xs !! l then r else l) $ swap i (if r < length xs && xs !! r > xs !! l then r else l) xs
where
l = 2 * i + 1
r = 2 * i + 2
swap i j xs = [if k == i then xs !! j else if k == j then xs !! i else xs !! k | k <- [0..length xs - 1]]
sort :: Ord a => [a] -> [a]
sort [] = []
sort xs = y : sort ys
where
(y:ys) = heapify xs
This implementation defines a heapify
function that turns a list into a heap, and a sort
function that sorts a list using the heap sort algorithm.
The heapify
function works by recursively transforming the list into a heap, starting from the middle of the list and working towards the beginning. It does this by repeatedly calling the heapifyOne
function, which restores the heap property to a single element and its children in the heap.
The sort
function works by repeatedly extracting the maximum element (the root of the heap) and placing it at the end of the result list, until the heap is empty.
You can test this implementation by calling sort with a list of elements:
sort [4, 6, 9, 8, 3, 5, 1, 7, 2]
This should return the sorted list [1, 2, 3, 4, 5, 6, 7, 8, 9]
.
Commentary
Very impressive, especially with the detailed description! This time, we do see tail recursion applied in the heapify
function.
List Functions
How does it do with a more abstract prompt? I came up with this question based on Day 1 of the recently concluded 2022 Advent of Code.
Write a Haskell function to solve the following problem. The input is a list of lists of integers. We want to find the three lists with the largest sums, and add their sums together. So the output is just an integer.
Chat GPT:
Here is a function that solves the problem you described:
largestThreeSums :: [[Int]] -> Int
largestThreeSums xs = sum $ take 3 $ sortBy (flip compare) $ map sum xs
This function first applies the sum
function to each sublist in xs
, producing a list of sums. It then sorts this list in descending order using the sortBy
function, and takes the first three elements using the take
function. Finally, it sums these three elements using the sum
function and returns the result.
To use this function, you can call it with a list of lists of integers as an argument:
largestThreeSums [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
This should return the result 33, which is the sum of the three largest sublists [7, 8, 9], [10, 11, 12], and [4, 5, 6]
.
Commentary
This is quite similar to my solution! So Chat GPT did an effective job parsing my description, and determining the right list functions to use to make an efficient, one-line solution.
Making a Server
For one more example, let's try doing something more practical, like a web server. This will require using libraries beyond Haskell's base packages. Once again, this works quite well!
How can I write a basic HTTP server in Haskell?
Chat GPT:
Here is an example of a basic HTTP server in Haskell using the wai
and wai-extra
libraries:
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types (status200)
import Blaze.ByteString.Builder (copyByteString)
import qualified Data.ByteString.UTF8 as BU
app :: Application
app request respond = respond $
case rawPathInfo request of
"/hello" -> hello
_ -> notFound
where
hello = responseBuilder
status200
[("Content-Type", "text/plain")]
(copyByteString "Hello, World!")
notFound = responseBuilder
status404
[("Content-Type", "text/plain")]
(copyByteString "Not Found")
main :: IO ()
main = do
putStrLn "Listening on port 3000"
run 3000 app
This server listens for incoming HTTP requests on port 3000, and responds with a "Hello, World!" message for requests to the /hello
path, and a "Not Found" message for all other paths.
To run this server, save the code to a file and compile it using the ghc
compiler:
ghc Server.hs
This will produce an executable file named Server
. To start the server, run the executable:
./Server
You should see the message "Listening on port 3000" printed to the console. You can then send HTTP requests to the server using a tool like curl
or by opening a web browser and going to http://localhost:3000/hello
. You should receive a "Hello, World!" response for the /hello
path, and a "Not Found" response for all other paths.
Conclusion
So overall, Chat GPT does quite well with these basic challenges! It would be interesting to take this further still and see if we could make our server program more and more complex, like adding custom functionality for different routes. But Chat GPT definitely seems useful enough to help with basic tasks, even in a less well-known language like Haskell!
As a final note, I did check (out of curiosity) to see if Chat GPT seemed to know much about this blog. The answer appeared to be a resounding "no", leading to a good laugh for me! I asked, "do you know about Monday Morning Haskell?" This was the response I got from Chat GPT (emphasis is mine):
Yes, I am familiar with the term "Monday Morning Haskell." It refers to a programming style or approach that involves using the Haskell programming language to solve problems or write software on a Monday morning, with the implication being that Haskell is a particularly well-suited language for starting the work week off with a clear and focused mind. Haskell is a functional programming language known for its strong static type system, expressive type inference, and purity (lack of side effects). Many developers find that programming in Haskell can help them write clearer, more concise, and more maintainable code, and that it is especially well-suited for tasks that require reasoning about abstract concepts or dealing with complex data.
I might just have to add that description to the home page!