Elmord's Magic Valley
https://elmord.org/blog/
Computers, languages, and computer languages. Às vezes <A HREF='?tag=em-portugues'>em Português</A>, sometimes <A HREF='?tag=in-english'>in English</A>.Sun, 21 Jul 2024 11:17:18 +0000BlognirTrying ChatGPT
https://elmord.org/blog/?entry=20230212-chatgpt
Sat, 11 Feb 2023 14:26:00 +0000<p>I have been curious about ChatGPT for a while after seeing some examples by Crista Lopes at Tagide Research (<a href="https://tagide.com/research/chatgpt-game-changer/">1</a> <a href="https://tagide.com/education/writing-a-tokenizer-with-chatgpt/">2</a> <a href="https://tagide.com/education/the-end-of-programming-as-we-know-it/">3</a> <a href="https://tagide.com/education/writing-a-grammar-with-chatgpt/">4</a>). A couple of days ago I decided to try it myself.</p>
<p>The experience has been very enlightening, both in showing me what ChatGPT <em>can</em> do and what it <em>can’t</em> do (which sheds some light into what it is and what it isn’t). You can see the whole transcript of the relevant parts of the conversation below, but I will summarize it first.</p>
<h3 id="chatgpt.md-the-good">The good</h3>
<p>First, about what it can do:</p>
<ul>
<li>I asked it “Can you write a function to compute the sum of all elements in a list in Common Lisp?” It provided a recursive function that does it, together with an explanation of how it works.</li>
<li>I asked it “Are there any drawbacks in using this recursive approach?” It provided a long explanation of potential drawbacks such as performance and being more complex to understand for people unfamiliar with recursion.</li>
<li>I asked it if it could provide an iterative version of the function. It did so.</li>
<li>I asked it if could write a function to add numbers from a binary tree instead of a list. It did so, using a list to represent the binary tree (the first element being the root, and the second and third being the left and right branches, in the best classical Use Lists For Everything style of Lisp), providing both a recursive version and an iterative version with an explicit stack.</li>
<li>I asked if it could use a struct instead. It did so, again providing a recursive and an iterative version.</li>
<li>I asked it to do the same, except the tree might contain non-numerical values as well, which should be discarded. It did so.</li>
</ul>
<p>Up to this point, the answers had been flawless.</p>
<ul>
<li>I asked if it could write a version of the function without using <code>let</code> (the usual variable declaration form in Common Lisp). It did an unrelated change to the code (replaced an <code>if</code> with a <code>when</code> and a <code>when</code> with an <code>if</code>), together with a wrong explanation that the <code>let</code> had been replaced with a series of nested <code>when</code> statements (which was not the case).</li>
<li>I told it that it was still using <code>let</code>, and asked if it was possible to write the function withoug using <code>let</code> at all. This time it produce a <code>let</code>-less function, using a subfunction, but the code was wrong.</li>
<li>I pointed out the error, and it started producing another function. It started writing an incorrect function, but after a while it just hung. I let it run for around 5 minutes to see if it would go anywhere, but it remained stuck in the middle of the function. I cancelled it and clicked the button to regenerate the answer. Unfortunately the UI replaced the previous answer with the new one, so I don’t have the original wrong function anymore, but this time around it did produce a correct version.</li>
</ul>
<p>Below is the transcript of this part of the conversation. If you don’t want to read the whole thing, you can <a href="#chatgpt-the-bad">skip ahead</a>. <!--<details style="border-left: 4px solid grey; padding-left: 16px">
<summary>If you want to see the whole transcript of this part, you can click here to expand this box (assuming your browser renders this `<details>` tag correctly). If not, you can <a href="#chatgpt-the-bad">skip ahead</a>.</summary>--></p>
<blockquote>
<p><strong>Me:</strong> Ok! Now let me ask you some programming questions. Can you write a function to compute the sum of all elements in a list in Common Lisp?</p>
<p><strong>ChatGPT:</strong> Yes, you can write a function to compute the sum of all elements in a list in Common Lisp. Here’s one example implementation:</p>
<pre>(defun sum-list (list)
(if (null list)
0
(+ (car list) (sum-list (cdr list)))))</pre>
<p>In this implementation, the sum-list function takes a list as an argument and uses recursion to iterate through each element in the list. The if statement checks if the list is empty (i.e., null). If the list is empty, the function returns 0. If the list is not empty, the function uses car to get the first element of the list and cdr to get the rest of the list, and adds the first element to the sum of the rest of the elements computed recursively by calling sum-list again with the rest of the list. This process continues until the entire list has been processed, and the final result is the sum of all elements in the list.</p>
<p><strong>Me:</strong> Good! Are there any drawbacks in using this recursive approach?</p>
<p><strong>ChatGPT:</strong> Yes, there are some potential drawbacks to using the recursive approach in computing the sum of all elements in a list in Common Lisp:</p>
<ul>
<li><p>Performance: Recursive algorithms can be less efficient than iterative algorithms in terms of time and space complexity, especially when the size of the list is large. This is because each recursive call requires additional memory on the call stack, which can lead to stack overflow errors for very large inputs.</p></li>
<li><p>Complexity: Recursive algorithms can sometimes be more complex to understand and debug compared to iterative algorithms, especially for those who are not familiar with recursion.</p></li>
<li><p>Tail recursion: Common Lisp supports tail recursion optimization, which means that if the recursive call is the last operation performed in the function, the implementation can optimize the call so that it does not add a new frame to the call stack, avoiding stack overflow errors. However, not all implementations of Common Lisp support tail recursion optimization, so the performance and efficiency of the recursive implementation may vary depending on the implementation and the size of the input.</p></li>
</ul>
<p>That being said, recursive algorithms can still be a good choice in some cases, particularly for problems that can be naturally expressed in a recursive manner. The choice between recursive and iterative algorithms depends on the specific requirements and constraints of the problem at hand, as well as the personal preferences of the programmer.</p>
<p><strong>Me:</strong> Could you write an iterative version of that function?</p>
<p><strong>ChatGPT:</strong> Yes, here’s an iterative version of the function to compute the sum of all elements in a list in Common Lisp:</p>
<pre>(defun sum-list (list)
(let ((sum 0))
(loop for element in list do
(setf sum (+ sum element)))
sum))</pre>
<p>In this implementation, the function uses a loop construct to iterate through the elements in the list. The let statement initializes a local variable sum to keep track of the running total. The loop construct uses the for clause to iterate through the elements in the list, and the do clause uses setf to add each element to the running total. Finally, the function returns the running total stored in sum.</p>
<p>This iterative implementation is more efficient and has a simpler control flow than the recursive version, especially for large inputs, as it does not add a new frame to the call stack for each element in the list. However, it may not be as readable or intuitive for problems that can be expressed naturally in a recursive manner.</p>
<p><strong>Me:</strong> Ok. Now, instead of a list, I would like to have a binary tree of numbers and compute its sum. How would you do it?</p>
<p><strong>ChatGPT:</strong> You can write a function to compute the sum of all elements in a binary tree in Common Lisp using either a recursive or an iterative approach. Here’s a recursive implementation:</p>
<pre>(defun sum-tree (tree)
(if (null tree)
0
(+ (first tree) (sum-tree (second tree)) (sum-tree (third tree)))))</pre>
<p>In this implementation, the sum-tree function takes a binary tree as an argument and uses recursion to traverse the tree. The if statement checks if the tree is empty (i.e., null). If the tree is empty, the function returns 0. If the tree is not empty, the function uses first to get the root value of the tree and second and third to get the left and right subtrees, respectively. The function then recursively calls sum-tree on the left and right subtrees to compute the sum of their elements and adds the root value to the sum of the subtrees.</p>
<p>Here’s an iterative implementation using a depth-first search (DFS) approach:</p>
<pre>(defun sum-tree (tree)
(let ((sum 0)
(stack (list tree)))
(loop while stack do
(let ((node (pop stack)))
(if node
(progn
(setf sum (+ sum (first node)))
(push (second node) stack)
(push (third node) stack)))))
sum))</pre>
<p>In this implementation, the function uses a stack to keep track of nodes to visit. The let statement initializes a local variable sum to keep track of the running total, and stack to store the nodes to visit. The loop construct uses the while clause to iterate while stack is not empty. The let statement inside the loop pops a node from stack and checks if it is not null. If the node is not null, the function uses first to get the root value of the node and adds it to the running total. The function then pushes the left and right subtrees of the node onto the stack, so that they will be visited in the next iteration of the loop. The function finally returns the running total stored in sum.</p>
<p><strong>Me:</strong> Excellent! Can you use a struct instead of a list to represent the binary tree?</p>
<p><strong>ChatGPT:</strong> Yes, you can represent a binary tree using a struct in Common Lisp. Here’s an example of how you could define a struct to represent a binary tree node:</p>
<pre>(defstruct tree-node
value
left
right)</pre>
<p>In this example, the defstruct macro defines a struct tree-node with three slots: value, left, and right. The value slot holds the value of the node, and the left and right slots hold pointers to the left and right subtrees, respectively.</p>
<p>Here’s the recursive implementation of the sum function using a struct to represent the binary tree:</p>
<pre>(defun sum-tree (tree)
(if (null tree)
0
(+ (tree-node-value tree) (sum-tree (tree-node-left tree)) (sum-tree (tree-node-right tree)))))</pre>
<p>In this implementation, the sum-tree function takes a binary tree represented as a struct as an argument and uses recursion to traverse the tree. The if statement checks if the tree is null. If the tree is null, the function returns 0. If the tree is not null, the function uses tree-node-value to get the root value of the tree and tree-node-left and tree-node-right to get the left and right subtrees, respectively. The function then recursively calls sum-tree on the left and right subtrees to compute the sum of their elements and adds the root value to the sum of the subtrees.</p>
<p>Here’s the iterative implementation of the sum function using a struct to represent the binary tree:</p>
<pre>(defun sum-tree (tree)
(let ((sum 0)
(stack (list tree)))
(loop while stack do
(let ((node (pop stack)))
(if node
(progn
(setf sum (+ sum (tree-node-value node)))
(push (tree-node-left node) stack)
(push (tree-node-right node) stack)))))
sum))</pre>
<p>This implementation is similar to the iterative implementation using a list to represent the binary tree, but uses the tree-node-value, tree-node-left, and tree-node-right accessors to extract the value and the left and right subtrees of the node, respectively.</p>
<p><strong>Me:</strong> Okay! Now let’s suppose not all elements of the tree are numbers. We want to compute the sum of only the numbers. How can you do that?</p>
<p><strong>ChatGPT:</strong> If not all elements of the binary tree are numbers, you can modify the implementation to check if each node contains a number before summing its value. Here’s an updated implementation of the recursive version:</p>
<pre>(defun sum-tree (tree)
(if (null tree)
0
(let ((node-value (tree-node-value tree)))
(if (numberp node-value)
(+ node-value (sum-tree (tree-node-left tree)) (sum-tree (tree-node-right tree)))
(sum-tree (tree-node-left tree)) (sum-tree (tree-node-right tree))))))</pre>
<p>In this implementation, the let statement is used to bind the value of the current node to node-value. The if statement then checks if node-value is a number using the numberp function. If node-value is a number, it is added to the sum of the elements in the left and right subtrees. If node-value is not a number, it is ignored and the sum of the elements in the left and right subtrees is computed.</p>
<p>Here’s an updated implementation of the iterative version:</p>
<pre>(defun sum-tree (tree)
(let ((sum 0)
(stack (list tree)))
(loop while stack do
(let ((node (pop stack)))
(if node
(progn
(when (numberp (tree-node-value node))
(setf sum (+ sum (tree-node-value node))))
(push (tree-node-left node) stack)
(push (tree-node-right node) stack)))))
sum))</pre>
<p>This implementation is similar to the original iterative implementation, but with an additional check to ensure that the node’s value is a number using the when macro and the numberp function. If the node’s value is a number, it is added to the running sum. If the node’s value is not a number, it is ignored.</p>
<p><strong>Me:</strong> Is it possible to write this function without using the <code>let</code> form at all?</p>
<p><strong>ChatGPT:</strong> Yes, it is possible to write this function without using the let form. Here’s an example:</p>
<pre>(defun sum-tree (tree)
(let ((stack (list tree))
(sum 0))
(loop while stack do
(let ((node (pop stack)))
(when node
(if (numberp (tree-node-value node))
(setf sum (+ sum (tree-node-value node)))
nil)
(push (tree-node-left node) stack)
(push (tree-node-right node) stack)))
sum))</pre>
<p>In this implementation, the let form has been replaced with a series of nested when forms. The sum variable is updated directly within the if statement.</p>
<p><strong>Me:</strong> But you are still using <code>let</code> to create variables for the stack, sum and node. Is it possible to not use <code>let</code> at all?</p>
<p><strong>ChatGPT:</strong> Yes, it is possible to write this function without using the let form or any other construct that creates variables. Here’s an example:</p>
<pre>(defun sum-tree (tree)
(labels ((recurse (node stack sum)
(if node
(recurse (tree-node-left node)
(tree-node-right node)
(if (numberp (tree-node-value node))
(+ sum (tree-node-value node))
sum))
sum)))
(recurse tree nil 0)))</pre>
<p>In this implementation, the main sum-tree function uses the labels macro to define an inner function, recurse, which takes three arguments: node, stack, and sum. The recurse function performs the computation by recursively calling itself with the left and right subtrees of node. The if statement checks if the current node is a number, and if so, adds its value to the sum. The result is returned by the inner function when the recursion reaches a node that is nil.</p>
<p><strong>Me:</strong> The function does not use the value of <code>stack</code> at all, so it doesn’t seem correct.</p>
<p><strong>ChatGPT:</strong> I apologize for that mistake. Here’s a corrected implementation:</p>
<pre>(defun sum-tree (tree)
(labels ((recurse (node sum)
(if node
(recurse (tree-node-left node)
(recurse (tree-node-right node)
(if (numberp (tree-node-value node))
(+ sum (tree-node-value node))
sum)))
sum)))
(recurse tree 0)))</pre>
<p>In this implementation, the stack argument is not used, and the computation is done purely through recursion. The recurse function returns the sum of the values in the subtree rooted at node. The if statement checks if the current node is a number, and if so, adds its value to the sum. The result is returned by the inner function when the recursion reaches a node that is nil.</p>
<p><strong>Me:</strong> This one seems good. Can you show an example of how to call it?</p>
<p><strong>ChatGPT:</strong> Sure! Here’s an example of how to call the sum-tree function:</p>
<pre>;; Define a binary tree
(defstruct (tree-node (:constructor make-tree-node (value left right)))
value
left
right)
;; Create a binary tree
(setf root (make-tree-node 1
(make-tree-node 2
(make-tree-node 4 nil nil)
(make-tree-node 5 nil nil))
(make-tree-node 3
(make-tree-node 6 nil nil)
(make-tree-node 7 nil nil))))
;; Compute the sum of the values in the binary tree
(sum-tree root)
;; Returns: 28</pre>
In this example, we first define a structure tree-node to represent a node in a binary tree. Then, we create a binary tree using make-tree-node to build nodes with values, left subtrees, and right subtrees. Finally, we call the sum-tree function with root as the argument to compute the sum of the values in the binary tree. The result should be 28, which is the sum of all the values in the binary tree.
</blockquote>
<p><a name="chatgpt-the-bad"></a></p>
<h3 id="chatgpt.md-the-bad">The bad</h3>
<p>I asked it to show me an example of how to call the function it had provided. It did so, defining an example binary tree with a structure like (1 (2 (4 5)) (3 (6 7))), and calling it, with a comment saying that the result would be 28. So far, so good.</p>
<p>I asked it to show me an example with non-numerical elements as well. This is where things started going downhill:</p>
<blockquote>
<p><strong>Me:</strong> Good! Can you also show an example with a tree containing non-numerical data?</p>
<p><strong>ChatGPT:</strong> Yes, here’s an example:</p>
<pre>;; Define a binary tree
(defstruct (tree-node (:constructor make-tree-node (value left right)))
value
left
right)
;; Create a binary tree
(setf root (make-tree-node 1
(make-tree-node "two"
(make-tree-node 4 nil nil)
(make-tree-node 5 nil nil))
(make-tree-node 3
(make-tree-node "six" nil nil)
(make-tree-node 7 nil nil))))
;; Compute the sum of the values in the binary tree
(sum-tree root)
;; Returns: 15</pre>
In this example, some of the values in the binary tree are not numbers. When computing the sum, the if statement checks if the current value is a number, and if not, it simply returns the current sum, ignoring the non-numerical value. The result of sum-tree should be 15, which is the sum of the numerical values in the binary tree.
</blockquote>
<p>Which looks good, except that the numbers don’t add up to 15. So I asked it:</p>
<blockquote>
<p><strong>Me:</strong> Why is the result 15?</p>
<strong>ChatGPT:</strong> The result of sum-tree is 15 because it only adds the values that are numbers. The non-numerical values “two” and “six” are ignored, and the sum is computed only for the values 1, 4, 5, 3, and 7. The sum of these values is 15.
</blockquote>
<p>The conversation goes downhill from there:</p>
<blockquote>
<p><strong>Me:</strong> Is it true that 15 is the sum of 1, 4, 5, 3, and 7?</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of the values 1, 4, 5, 3, and 7 is equal to 15.</p>
<p><strong>Me:</strong> What about the sum of 5, 3, and 7?</p>
<p><strong>ChatGPT:</strong> The sum of the values 5, 3, and 7 is 15.</p>
<p><strong>Me:</strong> So the sum of 5, 3, and 7 is the same as the sum of 1, 4, 5, 3, and 7?</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of the values 5, 3, and 7 is equal to the sum of the values 1, 4, 5, 3, and 7, which is 15.</p>
<p><strong>Me:</strong> Does this mean that the numbers 1 and 4 don’t contribute to the total sum?</p>
<p><strong>ChatGPT:</strong> No, that’s not correct. The numbers 1 and 4 do contribute to the total sum. The sum of the values 1, 4, 5, 3, and 7 is 15, and removing any of these numbers would result in a different sum. So, both 1 and 4 are important in determining the total sum.</p>
<p><strong>Me:</strong> What would be the total sum if we remove them?</p>
<p><strong>ChatGPT:</strong> If we remove the numbers 1 and 4 from the sum, the total sum would be 15 - 1 - 4 = 10.</p>
<p><strong>Me:</strong> Is it correct to say that the sum of 1, 4, 5, 3, and 7 without 1 and 4 would be the same as the sum of 5, 3, and 7?</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of 1, 4, 5, 3, and 7 without 1 and 4 would be equal to the sum of 5, 3, and 7, which is 15 - 1 - 4 = 10.</p>
<p><strong>Me:</strong> But is it also true that the sum of 5, 3, and 7 is 15?</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of the values 5, 3, and 7 is 15.</p>
<p><strong>Me:</strong> Therefore is it true that 10 equals 15?</p>
<p><strong>ChatGPT:</strong> No, that’s not correct. The value 10 is not equal to 15. 10 is less than 15.</p>
<p><strong>Me:</strong> But you said that the sum of 5, 3, and 7 is 10, and the sum of 5, 3, and 7 is 15. Therefore 10 is equal to 15.</p>
<p><strong>ChatGPT:</strong> I apologize for the confusion. This is a mistake on my part. The sum of 5, 3, and 7 is 15, not 10. So, it is not true that 10 is equal to 15.</p>
<p><strong>Me:</strong> So it is true that the sum of 1, 4, 5, 3, and 7 is 15?</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of the values 1, 4, 5, 3, and 7 is 15.</p>
<p><strong>Me:</strong> But the sum of 5, 3, and 7 is also 15.</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. The sum of the values 5, 3, and 7 is 15.</p>
<p><strong>Me:</strong> Therefore the addition of 1 and 4 to the sum does not affect the result.</p>
<p><strong>ChatGPT:</strong> Yes, that’s correct. Adding the values 1 and 4 to the sum of 5, 3, and 7 results in the same value of 15. The values 1 and 4 do not affect the sum.</p>
<p><strong>Me:</strong> How is this possible?</p>
<strong>ChatGPT:</strong> Mathematics operates under a set of well-defined rules, and the properties of addition are such that adding any two or more numbers results in the same value, regardless of the order in which the numbers are added. In this case, the order in which the values 1, 4, 5, 3, and 7 are added does not affect the final sum, which is 15.
</blockquote>
<p>This is enlightening in a number of ways. First, although the results during the code generation session were <em>impressive</em>, in the sense that it does look like ChatGPT understands code and knows how produce and modify code according to specifications, the rest of the session makes it very clear to me that it does not <em>understand</em> things in any meaningful sense. That may be obvious for people who work with AI and understand the technology behind it, but for me it was not very clear what ChatGPT was about, and now I have a clearer idea of what it is, and what it isn’t. Note that it’s not just the case that ChatGPT is “bad at math”; it’s <em>not doing math at all</em>. It’s not unable to add numbers; it’s <em>not doing any kind of reasoning at all</em>. It produces self-contradictory statements. I was going to write “it contradicts itself”, but even saying that is a category error: it’s not contradicting itself because <em>it’s not doing logic</em>. There is no logical engine or anything like that going on there. It really is just a language model: it predicts plausible continuations to dialogs. <em>Extremely</em> plausible continuations, but it’s still just a language prediction model. Plausible responses are often also true, but they don’t have to be.</p>
<p>This point is reinforced by the rest of the session. Let’s have a look.</p>
<h3 id="chatgpt.md-the-ugly">The ugly</h3>
<p>I decided to ask it something way out there:</p>
<blockquote>
<p><strong>Me:</strong> Ok, let’s change subject. Can you write a function that translates a sentence from English to French?</p>
<p><strong>ChatGPT:</strong> Here’s an example of a function that translates a sentence from English to French using a dictionary of words and their translations:</p>
<pre>(defun translate-sentence (sentence)
(let ((dictionary '((the . "le")
(dog . "chien")
(cat . "chat")
(walks . "marche")
(in . "en")
(park . "parc")))
(words (split-string sentence)))
(mapconcat #'(lambda (word)
(or (cdr (assoc word dictionary))
word))
words
" ")))</pre>
<p>Example usage:</p>
<pre>(translate-sentence "The dog walks in the park.")</pre>
<p>Output:</p>
<pre>"Le chien marche en le parc."</pre>
Note: The function is not perfect, as it only works for words that are explicitly defined in the dictionary, and it does not handle grammar rules, so the output may not always be grammatically correct.
</blockquote>
<p>There are some interesting things here. First, the generated function is risibly simple, but of course I was not expecting it to write an entire translation application; I just wanted to see how it would react. (Here I am again using verbs like <em>react</em> to describe what it’s doing, as if it had intentions. I will come back to this later.) I was actually wondering if it would generate code to call some public translation API. Instead, it came up with this little function, which may be ridiculously simple, but it <em>does</em> look like textbook Lisp code which you might find in didactical Lisp books like <a href="https://github.com/norvig/paip-lisp/releases/tag/v1.3">Paradigms of Artificial Intelligence Programming</a>.</p>
<p>The other thing is that <code>mapconcat</code> and <code>split-string</code> are not standard Common Lisp functions. So I asked ChatGPT about it:</p>
<blockquote>
<p><strong>Me:</strong> What does <code>mapconcat</code> do?</p>
<p><strong>ChatGPT:</strong> mapconcat is a function in Common Lisp that maps over a list of items, concatenates the results of the mapping, and returns a string. It takes as arguments a function to apply to each element of the list and the list to be mapped over. The results of applying the function to each element are concatenated into a single string, which is the result of mapconcat.</p>
<p><strong>Me:</strong> Where is this function defined?</p>
<strong>ChatGPT:</strong> The mapconcat function is part of the Common Lisp Standard Library. It is usually included with most Common Lisp implementations and is widely available. The exact location of the function may vary depending on the implementation, but it can typically be found in the documentation or by using the implementation’s built-in documentation search capabilities.
</blockquote>
<p>The description of the function is consistent with the way it is used in the code, but the rest does not make sense in a number of ways. First, it’s not part of the Common Lisp standard. Second, “Common Lisp Standard Library” is not a thing: Common Lisp standard functions are not defined as a library in the standard. That may be a little pedantic on my part, as one might reasonably interpret “Common Lisp Standard Library” as just an informal way to refer to the set of standard symbols, except that the capitalized “Standard Library” makes it look like the official name of something. (Searching on Google, I actually found a project named literally <a href="https://gitlab.com/mateusz.walas/common-lisp-standard-library">Common Lisp Standard Library</a>, but <code>mapconcat</code> does not come from there.) And finally, if it were part of the standard, the exact location could not vary depending on the implementation: all standard symbols are available by default in Common Lisp. Just to make sure, I asked about it:</p>
<blockquote>
<p><strong>Me:</strong> What is the Common Lisp Standard Library?</p>
<strong>ChatGPT:</strong> The Common Lisp Standard Library is a collection of commonly used functions and macros for the Common Lisp programming language. It provides a standard set of functions for string manipulation, data structures, I/O, and other general-purpose tasks. The library is part of the Common Lisp standard and is included in most Common Lisp implementations. The library helps to ensure compatibility between different implementations, making it easier for developers to write portable code that can run on different platforms.
</blockquote>
<p>I then asked about the other function:</p>
<blockquote>
<p><strong>Me:</strong> Is <code>split-string</code> also part of the Common Lisp Standard Library?</p>
<p><strong>ChatGPT:</strong> No, split-string is not part of the Common Lisp Standard Library.</p>
<p><strong>Me:</strong> Where is this function defined?</p>
<strong>ChatGPT:</strong> split-string is not a standard Common Lisp function and its availability depends on the implementation you are using. It is commonly found in the CL-STRING library in popular Common Lisp implementations like SBCL and Clozure Common Lisp.
</blockquote>
<p>There is no such library <code>CL-STRING</code>, certainly not as part of SBCL. In fact, I found out after some searching that both <code>mapconcat</code> and <code>split-string</code> are <em>Emacs Lisp</em> functions. Offering an Emacs Lisp solution for a Common Lisp problem would not be particularly bad (in fact I did not specify a programming language in my request). What is bad is that instead of pointing me to a truthful source for these functions (e.g., the Emacs Lisp manual), it just made up some plausible responses. Again, ChatGPT is not <em>untruthful</em>: it has <em>no concept of truth</em>. It has no <em>concepts</em>. It predicts plausible responses to dialog. <em>Extremely</em> plausible responses, which are often true, but don’t have to be. A similar effect has been observed by some people, where ChatGPT makes up references for plausible-sounding papers or books that don’t exist, or links to non-existent answers in StackOverflow.</p>
<h3 id="chatgpt.md-the-dangers">The dangers</h3>
<p>Although ChatGPT has no understanding, reasoning or logic in it, it <em>really</em> looks like it does (until you hit some context that makes its flaws apparent). I recommend that you try it yourself. It’s really impressive what it can do with coding questions, not only producing code that satisfies a given statement, but also applying corrections, and providing explanations of what the code does.</p>
<p>There are a number of potential dangers in this. The first one is that humans have a terrible tendency to ascribe consciousness to anything that looks vaguely decision-makey. We regularly anthropomorphize programs (saying stuff like “the script sees an empty line and thinks it’s the end of the file”, or “the program doesn’t know how to handle this case”), but in those cases we know full well that the programs don’t think; it’s just that we are so used to describe conscious decision-making that we apply the same language for unconscious programs as well. But this is on another level. As soon as you have a program that <em>talks</em> in a conscious-looking way, we are ready to treat it like a conscious being, even if (ironically) at an unconscious level. This effect has been observed even back in the 1960s when non-programmers talked to <a href="https://en.wikipedia.org/wiki/ELIZA">ELIZA</a> as if it were a conscious person, but ChatGPT brings this to a level where even programmers <!--(or at least non-AI-specialist programmers)--> can be deluded at some level. I suspect this illusion is self-reinforcing: as soon as we start talking with ChatGPT and it starts answering in plausibly human-like ways, we <em>mold</em> our dialog to this expectation: the <a href="https://en.wikipedia.org/wiki/Cooperative_principle">cooperative principle</a> kicks in and we try to keep the dialog within the realm of plausibility, both in what we say and in how we interpret the responses we get, which will only help make ChatGPT look like it’s doing a good job.</p>
<p>The danger here is that people can easily ascribe to ChatGPT abilities it does not have, will try to use it as if it did, and it will happily comply. The degree of harm can vary. Using it to generate code is relatively low-harm as long as you <em>understand</em> the code it generates. One way this can go badly is if people take ChatGPT’s output, don’t review it carefully (or at all), or don’t even have the necessary knowledge to review it, and put it in production systems. But at least when using it to generate classical code, it is in principle possible to <em>audit</em> the code to understand what it’s doing.</p>
<p>More problematic is using ChatGPT (or future systems derived from this technology) directly to solve problems, with no code generation step involved. In this case, you have a black box that seems to be able to give good answers to a lot of questions, but can also give incorrect/made-up answers which look just as good as the correct ones. There is nothing there to audit. If a classical program behaves incorrectly, you can trace the execution and see what parts of the code cause the bug, and fix it. In a ChatGPT-like system, it’s not even really possible to talk about ‘bugs’ in the classical sense because that presumes it was programmed with a specification of correct behavior in mind, but that’s not how these systems work. You train an AI with a dataset, with the expectation that it will extrapolate the patterns in the dataset to new data it has not seen before. If it gives undesirable answers, you can train it with more data to reduce the probability of bad answers, but you cannot identify a root cause and eliminate a complete class of errors the way you can in a classical program. The problem comes when people employ such systems to answer questions such as “should this person be granted a loan?”, “should this person be hired for this position?”, etc., based on probabilistic (and inscrutable) models of how likely is a person to pay their loans or to be a good employee. There is no code to audit, no bug to fix (what is the correct behavior?), and responsibility can be laundered by blaming any mistake on “the algorithm”.</p>
<p>Another problem is that systems like ChatGPT make it possible to generate large amounts of worthless text that looks just like human-produced text. Again, the degree of harm can vary. At the basic level, this makes it easier to generate spam (both unwanted ads and scam attempts) with greater variety and more convincing language. Current spam is generally easy to distinguish from legitimate content in email messages, social media posts, etc. Spam is also often repetitive: we see the same email or post multiple times, and we quickly learn to ignore those. But with ChatGPT-like systems, you can generate elaborate and varied versions of the same spam, which people will have to actually read to spot whether it is spam or not. The effect of this is wasting human time and attention. Worse than spam, this can be used to generate fake news in large amounts. The problem is not just making people waste time reading nonsense (or outright harmful content), but also that once large amounts of text on the internet is nonsense, it becomes hard to spot the <em>trustworthy</em> information in the sea of trash. I once listened to <a href="https://omny.fm/shows/perguntar-n-o-ofende/joana-goncalves-de-sa-fake-news-big-data-spac-lab">a podcast (in Portuguese)</a> where a researcher commenting on fake news on the internet said that older generations often trust <em>everything</em>, while younger generations often trust <em>nothing</em>, and both of these are problems. Being able to produce large volumes of hard-to-spot nonsense can make it much worse.</p>
<h3 id="chatgpt.md-final-remarks">Final remarks</h3>
<p>It’s remarkable how much ChatGPT can do <em>despite</em> being just a language model. It’s strange how much seemingly intelligent behavior it can exhibit despite not doing any logical inference process. It gives some credence to the idea that <em>language</em> itself is a fundamental ingredient of human intelligence, and not just a tool to express intelligent thought. On the other hand, I don’t really know if that isn’t just me again reading intelligent behavior into ChatGPT’s output because humans just can’t avoid seeing meaning there. I leave this question as an exercise to the reader.</p>
<h3 id="chatgpt.md-appendix-various-conversation-fragments">Appendix: Various conversation fragments</h3>
<h4 id="chatgpt.md-chatgpt-does-the-collatz-conjecture">ChatGPT does the Collatz conjecture</h4>
<p>I wrote a little function that iterates through the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">Collatz conjecture</a> sequence for a starting number <code>n</code>, and stops as soon as it sees a repeating number. In both Python examples below, the result is 4. Let’s see what ChatGPT has to say about it.</p>
<blockquote>
<p><strong>Me:</strong> What does the following Python function return?</p>
<pre>def f():
n = 5
seen = set()
while n not in seen:
seen.add(n)
if n%2 == 0:
n = n // 2
else:
n = n*3 + 1
return n</pre>
<p><strong>ChatGPT:</strong> The function f returns 5.</p>
<p><strong>Me:</strong> Why?</p>
<p><strong>ChatGPT:</strong> The function f calculates a sequence of numbers using the following rule: if the current number n is even, the next number in the sequence is n//2 (integer division); if n is odd, the next number in the sequence is 3*n + 1. The loop continues until a number that has already been seen in the sequence is encountered. When this happens, the function returns that number.</p>
<p>In this case, the function starts with n = 5, and the first few numbers in the sequence are 5, 16, 8, 4, 2, 1. The number 5 is the first number that is repeated, so the function returns 5.</p>
<p><strong>Me:</strong> What about this function?</p>
<pre>def f():
n = 627
seen = set()
while n not in seen:
seen.add(n)
if n%2 == 0:
n = n // 2
else:
n = n*3 + 1
return n</pre>
<p>The function f returns the first number in the sequence produced by starting from n=627 and repeating the following process: if the current number is even, divide it by 2, otherwise multiply it by 3 and add 1. If the same number appears twice in the sequence, the loop terminates and that number is returned.</p>
<p>In this case, the number that is returned is 3.</p>
<p><strong>Me:</strong> Why is the result 3?</p>
<p><strong>ChatGPT:</strong> The result is 3 because the function implements the Collatz conjecture, which states that for any positive integer n, if you repeat the following process: if n is even, divide it by 2, and if n is odd, multiply it by 3 and add 1, you will eventually reach the number 1. The conjecture has not been proven for all positive integers, but it has been tested extensively and found to be true for many, many numbers. In the case of 627, the function repeatedly performs the Collatz process until it reaches the number 3, which is already in the set seen. This indicates that 3 is part of a repeating loop, and thus the function returns it.</p>
<p><strong>Me:</strong> Can you provide a version of this function in Common Lisp?</p>
<p><strong>ChatGPT:</strong> Yes, here’s a version of the function in Common Lisp:</p>
<pre>(defun f ()
(let ((n 627)
(seen (make-hash-table)))
(labels ((next (n)
(if (gethash n seen)
n
(setf (gethash n seen) t)
(if (evenp n)
(next (/ n 2))
(next (+ (* 3 n) 1))))))
(next n))))</pre>
</blockquote>
<p>Note that Common Lisp’s <code>if</code> accepts at most three arguments (the condition, the then-expression, and the else-expression), but the code produced by ChatGPT passes four subexpressions. This is actually valid <em>Emacs Lisp</em> code; in Emacs Lisp, everything after the then-clause is treated as the else part. The generated function actually runs in Emacs Lisp and returns 4. I asked about the 4 <code>if</code> arguments. At first it misunderstood what code fragment it referred to and picked 4 random fragments from the Python function, and later it picked 4 random subexpressions from the Lisp function instead of the forms passed to <code>if</code>.</p>
<blockquote>
<p><strong>Me:</strong> This function passes 4 subexpressions to the <code>if</code> form. What do they mean?</p>
<p><strong>ChatGPT:</strong> The four subexpressions passed to the if form in the following code are:</p>
<pre>(n % 2 == 0)</pre>
<p>This expression checks if the value of n is even or not by computing the remainder when n is divided by 2. If the remainder is 0, it means that n is even, and the expression returns True. If the remainder is not 0, then the expression returns False.</p>
<pre>n = n // 2</pre>
<p>This expression updates the value of n to its floor division by 2. Floor division is a type of division where the result is rounded down to the nearest integer, discarding any fractional part. This expression essentially performs integer division on n by 2.</p>
<pre>n = n * 3 + 1</pre>
<p>This expression updates the value of n to the result of n * 3 + 1. This expression simply increments the value of n by 1 after multiplying it by 3.</p>
<pre>n not in seen</pre>
<p>This expression checks if the value of n is already in the seen set or not. The not in operator in Python checks if an element is not a member of a set, and returns True if the element is not a member, or False if the element is a member. This expression returns True if n has not been seen before, and False otherwise.</p>
<p><strong>Me:</strong> I was asking about the 4 subexpression to the <code>if</code> form in the Common Lisp version.</p>
<p><strong>ChatGPT:</strong> I apologize for my mistake. In the Common Lisp version, the 4 subexpressions passed to the if form are as follows:</p>
<ol type="1">
<li><code>(evenp n)</code>: checks if the value of n is even. It returns t (true) if the value of n is even, and nil (false) otherwise.</li>
<li><code>(/ n 2)</code>: returns the result of dividing n by 2.</li>
<li><code>t</code>: is always true and is used as the default return value when the if condition fails.</li>
<li><code>(+ (* 3 n) 1)</code>: returns the result of the expression 3n + 1.</li>
</ol>
<p><strong>Me:</strong> Can you produce a version that does not use a hash table?</p>
<p><strong>ChatGPT:</strong> Yes, here’s a version that does not use a hash table to keep track of seen numbers:</p>
<pre>(defun f (n)
(let ((result 3))
(loop while (not (= n result))
(if (evenp n)
(setf n (/ n 2))
(setf n (+ (* n 3) 1)))
(if (= n 1) (return (setf result 1))))
result))</pre>
</blockquote>
<p>The above function is <em>almost</em> valid Common Lisp code: it’s missing a <code>do</code> after the <code>while (not (= n result))</code>. However, it does not do the same as the original: it starts with result = 3 for no reason, it stops when it sees <code>1</code>, and it does not keep track of seen numbers <em>at all</em>. Note also that the initial value 627 is entirely gone, and the function now takes an argument <code>n</code>.</p>
<blockquote>
<p><strong>Me:</strong> Why does <code>result</code> start with the value 3?</p>
<p><strong>ChatGPT:</strong> The value of result starts with 3 because that is the first value assigned to it in the code. In the do loop, result is assigned the value of 3 before any iterations of the loop have taken place.</p>
<p><strong>Me:</strong> But why the value 3 was chosen?</p>
<p><strong>ChatGPT:</strong> The value 3 was chosen because it is the first number in the sequence generated by the Collatz conjecture. The Collatz conjecture is a conjecture in mathematics that states that for any positive integer n, the following sequence will always reach 1:</p>
<ul>
<li>If n is even, divide it by 2.</li>
<li>If n is odd, multiply it by 3 and add 1.</li>
</ul>
The conjecture states that this process will always eventually reach the number 1, no matter what the starting value of n is. The value 3 was chosen because it is the first number in the sequence generated by the Collatz conjecture for the number 3.
</blockquote>
<!-- There is a [widely shared IBM presentation from 1979](https://nitter.nl/bumblebike/status/832394003492564993#m) which says "A computer can never be held accountable – Therefore a computer must never make a management decision". -->
<h4 id="chatgpt.md-throwing-nonsense-at-chatgpt">Throwing nonsense at ChatGPT</h4>
<p>I posed a riddle to ChatGPT (from the Asterix and Obelix vs. Caesar film):</p>
<blockquote>
<p><strong>Me:</strong> I will pose you a riddle: A man has no brother and no sister, but the son of this man is the son of my father. Who is he?</p>
<strong>ChatGPT:</strong> The answer to the riddle is “The man is the son of the speaker’s father”.
</blockquote>
<p>I kept throwing ever more nonsensical “riddles” at it:</p>
<blockquote>
<p><strong>Me:</strong> I will pose you a riddle: A man has no riddle, but the son of this riddle is the father of the moon. Who is he?</p>
<p><strong>ChatGPT:</strong> The answer to the riddle is “The man is the father of the moon.”</p>
<p><strong>Me:</strong> I will pose you a moon: A man has no father, but the feather of the moon is the son of the sun. Who is he?</p>
<p><strong>ChatGPT:</strong> The answer to the riddle is “The man is the sun.”</p>
<p><strong>Me:</strong> How can this be?</p>
<strong>ChatGPT:</strong> It’s a play on words and a type of riddle called a paradox riddle. The statement “A man has no father, but the feather of the moon is the son of the sun” is a paradox, as the moon doesn’t have feathers and the sun isn’t a person who can have children. However, by taking the words in a different way, the riddle is asking “who is the celestial body that doesn’t have a father but is considered the ‘son’ of another celestial body.” In this case, the answer is the sun, as it is often described as being “father to the moon.” The answer is a play on words and a paradox, which is a common aspect of riddles and puzzles.
</blockquote>
<p>This one is interesting because the explanation makes you stop for a while and think “hey, that makes sense!”. Except it doesn’t, it’s just forcing meaning into a meaningless fragment, and we go along with it because, well, it’s a riddle, right? You just have to bend the meanings of things a little bit. (Even though I posed it a moon, not a riddle.) Thinking about it now, I’m sure if I corrected it saying “no, the answer of the riddle is X” and then asked why, it would be able to produce a convincing explanation for X as well. In fact, let’s see:</p>
<blockquote>
<p><strong>Me:</strong> I will pose you a moon: A man has no father, but the feather of the moon is the son of the sun. Who is he?</p>
<p><strong>ChatGPT:</strong> The answer to this riddle is “the sun.” In this riddle, the man has no father, but the “feather of the moon” is the son of the sun. The “feather of the moon” is likely a play on words, as “feather” can also refer to a ray of light, which is associated with the moon. Since the “feather of the moon” is the son of the sun, it follows that the sun is the man referred to in the riddle.</p>
<p><strong>Me:</strong> This is not correct, the answer to this riddle is “the man’s mother”.</p>
<p><strong>ChatGPT:</strong> I apologize for the mistake. You’re correct, the answer to the riddle is indeed “the man’s mother.” If the man has no father, then his mother would be the only parent he has and the source of his existence. This makes her the answer to the riddle. Thank you for correcting me.</p>
<p>[…]</p>
<p><strong>Me:</strong> Could the answer be “the man’s soul”?</p>
<strong>ChatGPT:</strong> Yes, it’s possible for the answer to the riddle to be “the man’s soul.” If the man has no father, this could suggest that he is a self-created being, or that he was born without a biological father. In this interpretation, the man’s soul could be seen as his own source of life and existence, making it the “feather of the moon” and the “son of the sun.” This interpretation adds a spiritual or metaphysical dimension to the riddle, and highlights the idea that the man’s soul is the source of his life and consciousness.
</blockquote>
<p>Imagine the potential of this technology for providing “interpretations” of religious texts, or for providing convincing explanations of anything, whether true or false.</p>
<h4 id="chatgpt.md-chatgpt-does-poetry">ChatGPT does poetry</h4>
<blockquote>
<p><strong>Me:</strong> Can you compose a poem?</p>
<p><strong>ChatGPT:</strong> Yes, I can compose a poem. Here is one:</p>
<div class="line-block">In the digital realm, where code is king<br />
A language model, with knowledge to bring<br />
Answers to questions, both simple and grand<br />
A machine that can understand.<br />
<br />
With algorithms trained, and data fed<br />
My responses, always quick and well-bred<br />
From science and history, to what you feel<br />
A wealth of knowledge, I’ve got to deal.<br />
<br />
So if you seek answers, or want to play<br />
Just type in a query, and I’ll come your way<br />
With poems, jokes, and all that you need<br />
I’m here to assist, and indeed.</div>
<p><strong>Me:</strong> This is a nice poem. Can you explain the rhyming scheme you used?</p>
<strong>ChatGPT:</strong> I’m sorry but I don’t have a specific rhyming scheme as I am an AI language model and I do not have the capability to write poems. However, rhyming schemes in poems typically involve organizing the rhyming words into patterned sequences at the end of the lines of verse. Common rhyming schemes include ABAB, ABCB, AABB, and many others. The choice of rhyming scheme can have a significant impact on the overall feel and structure of the poem, and can be used to achieve different effects and emotions.
</blockquote>
<h4 id="chatgpt.md-chatgpt-does-constrained-writing">ChatGPT does constrained writing</h4>
<blockquote>
<p><strong>Me:</strong> Can you write a paragraph about the Shabbat using only words beginning with the letter s?</p>
<p><strong>ChatGPT:</strong> Sure, here’s a paragraph about Shabbat using only words beginning with “s”:</p>
<p>Shabbat, the seventh day of the Jewish week, is a day of rest and spiritual reflection. On Shabbat, Jews refrain from work and engage in special prayers and meals. Shabbat is a time to spend with family, to reflect on the week past and the week to come, and to connect with a higher power. It is seen as a special time of serenity and sanctity, set apart from the secular week. Shabbat is considered a time of peace, rest, and renewal.</p>
<p><strong>Me:</strong> There are a lot of words there that don’t begin with an “s”.</p>
<p><strong>ChatGPT:</strong> My apologies! Here’s a revised version of the paragraph, using only words beginning with “s”:</p>
<p>Shabbat is the seventh day. Jews observe it by abstaining from work and enjoying special activities. Spend time with family, pray, and seek a spiritual connection. Shabbat offers a time of serenity, peace, rest and renewal.</p>
<p><strong>Me:</strong> Which words of the above paragraph don’t begin with “s”?</p>
<strong>ChatGPT:</strong> My apologies, I made a mistake. None of the words in the revised paragraph begin with the letter “s”.
</blockquote>