255 lines
9 KiB
Org Mode
255 lines
9 KiB
Org Mode
#+title: My first ChatGPT exchange
|
||
#+description: My first interaction with ChatGPT about Clojure and Monads.
|
||
#+keywords: machine-learning ai gpt
|
||
#+author: Yann Esposito
|
||
#+email: yann@esposito.host
|
||
#+date: [2022-12-11 Sun]
|
||
#+lang: en
|
||
#+options: auto-id:t
|
||
#+startup: showeverything
|
||
|
||
#+macro: logo @@html:<img src="/logo.svg" alt="me" style="width:1.5em;filter:none;"/>@@
|
||
|
||
* The first few questions for ChatGPT
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: the-first-few-questions-for-chatgpt
|
||
:END:
|
||
|
||
I finally opened an account to beta-test ChatGPT.
|
||
I must say, I am quite impressed.
|
||
I asked it a few questions that I could have asked during an interview.
|
||
To my surprise, the answer given was, while wrong, believable enough to kind of
|
||
sneak the answer as right up to some imprecision.
|
||
Like if you squint enough, it could pass, but if you pay attention it is clearly
|
||
false.
|
||
|
||
So yes, mainly ChatGPT in this interaction was quite good at being convincing.
|
||
This is the same kind of impression you have from looking at AI generated images.
|
||
At first sight everything seems fine, but if you start looking to the details, many
|
||
things are off.
|
||
|
||
So here is the full interaction, I'll let the attentive reader find the
|
||
technical problems:
|
||
|
||
|
||
{{{logo}}} Can you write a clojure function that will reverse any string provided to it.
|
||
|
||
#+begin_quote
|
||
|
||
*ChatGPT*: Sure, here's a possible implementation of a function that reverses a string in Clojure:
|
||
|
||
#+begin_src clojure
|
||
(defn reverse-string [s]
|
||
(apply str (reverse (seq s))))
|
||
#+end_src
|
||
|
||
This function takes a strings as input, converts it to a sequence of characters
|
||
using the ~seq~ function, reverses this sequence using the reverse function, and
|
||
then converts the resulting sequence back to a string using the ~apply~ and ~str~
|
||
functions.
|
||
|
||
Here's an example of how you can use this function:
|
||
#+begin_src clojure
|
||
user=> (reverse-string "hello world")
|
||
"dirow olleh"
|
||
#+end_src
|
||
|
||
|
||
Note that this function only works
|
||
for strings that contain characters in the Unicode Basic Multilingual Plane
|
||
(BMP), since the ~seq~ function only works on strings that contain characters in
|
||
this range.
|
||
If you need to reverse strings that contain characters outside of the BMP, you
|
||
can use the ~string-seq~ function instead of ~seq~ to convert the string to a
|
||
sequence of characters.
|
||
#+end_quote
|
||
|
||
{{{logo}}} How would this function react if instead of string you provide an integer?
|
||
|
||
#+begin_quote
|
||
*ChatGPT:* If you provide an integer as input to the ~reverse-string~ function instead of a
|
||
string, the function will fail with a ~ClassCastException~ because the ~seq~
|
||
function only works on strings, and you cannot convert an integer to a string in
|
||
a straightforward way in Clojure.
|
||
The ~seq~ function expects its input to be a string or a collection, such as a
|
||
vector or a list, and when you try to pass an integer to it, it will try to
|
||
convert the integer to a string by calling the ~str~ function on it.
|
||
However, since ~str~ only works on strings and collections, and not on numbers,
|
||
this will result in a ~ClassCastException~.
|
||
|
||
Here's an example of what will happen if you try to pass an integer to the reverse-string function:
|
||
|
||
#+begin_src clojure
|
||
user=> (reverse-string 12345)
|
||
ClassCastException clojure.lang. Numbers cannot be cast to
|
||
clojure.lang. IFn clojure.core/seq (core.clj:136)
|
||
#+end_src
|
||
|
||
To avoid this error, you can either make sure that the input to the
|
||
~reverse-string~ function is always a string, or you can modify the function to
|
||
handle other types of input, such as integers, by first converting them to
|
||
strings using the ~str~ function.
|
||
For example, you could write the ~reverse-string~ function like this to handle
|
||
both strings and integers as input:
|
||
|
||
#+begin_src clojure
|
||
(defn reverse-string [s]
|
||
(let [s (if (string? s) s (str s))]
|
||
(apply str (reverse (seq s)))))
|
||
#+end_src
|
||
|
||
With this version of the function, if you pass an integer as input, the function
|
||
will convert it to a string using the ~str~ function before calling the ~seq~
|
||
function on it, so the ~seq~ function will be able to work with the input.
|
||
Here's an example of how you can use this modified version of the
|
||
~reverse-string~ function:
|
||
|
||
#+begin_src clojure
|
||
user-> (reverse-string "hello world")
|
||
"dlrow olleh"
|
||
|
||
user=> (reverse-string 12345)
|
||
"54321"
|
||
#+end_src
|
||
|
||
Note that the modified version of the function will also work for other types of
|
||
input that can be converted to strings using the ~str~ function, such as floats
|
||
and booleans.
|
||
|
||
#+end_quote
|
||
|
||
{{{logo}}} How would you use the concept of the Maybe monad in Haskell in this clojure function?
|
||
|
||
#+begin_quote
|
||
*ChatGPT*: The Maybe monad in Haskell is a way to represent computations that may or may
|
||
not have & p a result, and it provides a way to compose these computations in a
|
||
safe and predictable way.
|
||
In Clojure, the Maybe monad is usually implemented as a ~Option~ type, which has
|
||
two possible values: ~Some~ and ~None~
|
||
|
||
In the context of the ~reverse-string~ function, you could use the ~Option~ type
|
||
to represent the possibility that the input to the function might not be a
|
||
string, in which case the function would return ~None~ instead of a reversed
|
||
string.
|
||
Here's an example of how you could use the ~Option~ type to modify the
|
||
~reverse-string~ function to handle the case where the input is not a string:
|
||
|
||
#+begin_src clojure
|
||
(defn reverse-string [s]
|
||
(if (string? s)
|
||
(Some (apply str (reverse (seq s))))
|
||
None))
|
||
#+end_src
|
||
|
||
With this version of the ~reverse-string~ function, if the input is a string, the
|
||
function will return a ~Some~ value containing the reversed string, and if the
|
||
input is not a string, the function will return a ~None~ value.
|
||
This allows the caller of the function to handle the case where the input is not
|
||
a string in a predictable and safe way.
|
||
|
||
Here's an example of how you can use this modified version of the ~reverse-string~
|
||
function:
|
||
|
||
#+begin_src clojure
|
||
user=> (reverse-string "hello world")
|
||
|
||
Some "dlrow olleh"
|
||
user=> (reverse-string 12345)
|
||
None
|
||
#+end_src
|
||
|
||
Note that the Option type in Clojure does not provide the same syntactic sugar
|
||
for composing computations as the Maybe monad in Haskell, so you cannot use the
|
||
~>>=~ operator to chain calls to the ~reverse-string~ function in the same way you
|
||
can in Haskell.
|
||
However, you can still use the ~Option~ type to represent computations that may
|
||
or may not have a result, and you can use the ~match~ or ~case~ forms to pattern
|
||
match on the ~Option~ values and handle the different cases in a predictable and
|
||
safe way.
|
||
|
||
#+end_quote
|
||
|
||
{{{logo}}} How would you implement Option in clojure?
|
||
|
||
#+begin_quote
|
||
*ChatGPT*:
|
||
In Clojure, the ~Option~ type is typically implemented as a sealed protocol,
|
||
which is a type of polymorphic type that defines a set of concrete types that
|
||
implement the protocol.
|
||
|
||
The ~Option~ protocol typically defines two concrete types: ~Some~, which represents
|
||
a computation that has a result, and ~None~, which represents a computation that
|
||
does not have a result.
|
||
|
||
Here's an example of how you could define the ~Option~ protocol and the ~Some~ and
|
||
~None~ types in Clojure:
|
||
|
||
#+begin_src clojure
|
||
(defprotocol Option
|
||
(map [o f])
|
||
(flat-map [o f]))
|
||
|
||
(deftype Some [x]
|
||
Option
|
||
(map [o f] (Some (f x)))
|
||
(flat-map [o f] (f x)))
|
||
|
||
(deftype None [])
|
||
Option
|
||
(map [o f] None)
|
||
(flat-map [o f] None))
|
||
#+end_src
|
||
|
||
The ~Option~ protocol defines two methods: ~map~ and ~flat-map~ The ~map~ method takes a
|
||
function ~f~ as input and applies it to the value contained in the ~Some~ type, ~f~ it
|
||
exists, or returns a ~None~ value ~f~ the ~Option~ type is ~None~ The ~flat-map~ method is
|
||
similar to ~map~ but it allows the function ~f~ to return an ~Option~ value, which is
|
||
then flattened into the outer ~Option~ value.
|
||
|
||
The ~Some~ type implements the ~Option~ protocol by providing concrete
|
||
implementations ~f~ the map and ~flat-map~ methods.
|
||
The 'map method applies the function ~f~ to the value ~x~ contained in the ~Some~
|
||
type, and returns a new ~Some~ value containing the result ~f~ the function.
|
||
The ~flat-map~ method applies the function ~f~ to the value ~x~ contained in the Some
|
||
type, and then flattens the resulting ~Option~ value into the outer ~Option~ value.
|
||
|
||
The ~None~ type also implements the Option protocol by providing concrete
|
||
implementations of the ~map~ and ~flat-map~ methods.
|
||
The ~map~ and ~flat-map~ methods simply return a ~None~ value in this case, since
|
||
there is no value to apply the function to.
|
||
|
||
Here's an example of how you can use the ~Option~ protocol and the ~Some~ and ~None~ types:
|
||
|
||
#+begin_src clojure
|
||
user=> (def some-value (Some 12345))
|
||
#' user/some-value
|
||
|
||
user=> (map some-value inc)
|
||
Some 12346
|
||
|
||
user=> (flat-map some-value (fn [x] (Some (* × 2))))
|
||
Some 24690
|
||
|
||
user=> (map some-value (comp (partial * 2) inc))
|
||
Some 24690
|
||
|
||
user=> (def none-value None)
|
||
#'user/none-value
|
||
|
||
user=> (map none-value inc)
|
||
None
|
||
|
||
user=> (flat-map none-value (fn [x] (Some (* × 2))))
|
||
None
|
||
|
||
user=> (map none-value (comp (partial * 2) inc))
|
||
None
|
||
#+end_src
|
||
|
||
In this example, the ~some-value~ variable is defined as a ~Some~ value containing
|
||
the integer 12345, and the ~none-value~ variable is defined as a ~None~ value.
|
||
The ~map~ and ~flat-map~ functions are then used to apply different functions to the
|
||
values contained in the ~Option~ types, and the...
|
||
#+end_quote
|
||
|