semiosis — Signs, Natural Language and Coding


λ (Imaginary Programming Functions)

Summary

tl;dr: Imaginary Programming (IP) is where
data and knowledge has been substituted for
inference by a LM. Therefore, the
implementation of an λ library will be
uniquely tailored to each language.

I design and build an IP library named
λ.el for emacs. Think of it a bit like a
functional programming library in that you
will find a set of functions and macros for
working in the programming paradigm. The
objective here is to create some functions for
doing IP in emacs lisp, since emacs lisp has
the expressive power to prototype such things,
but the ideas contained here can easily be
transferred to any other programming language.
The results of this little experiment will go
straight into my thesis.

λ project
http://github.com/semiosis/ilambda/
IP thesis
https://github.com/semiosis/imaginary-programming-thesis/blob/master/thesis.org
IP library
http://github.com/semiosis/pen.el/blob/master/src/ilambda.el
Glossaries
Imaginary Programming ⚔ Pen.el ⚔ Prompt Engineering ⚔ Epistemology

imacro (like regular macros) are for
generating code. idefun, however, doesnt
generate the code but attempts to evaluate a
function without code, or if code is supplied
then imagine evaluating that code rather than
actually evaluating it.

It’s kinda crazy that Im-macros run faster
and are more reliable than Im-functions,
where the opposite is true in regular
programming. That’s because they generate the
code which can be reused, adjusted and
interactively regenerated, where an
Im-function will typically have to query the
LM every time you call it.

The objective with λ.el

The objective here is to create IP functions
for programming in emacs lisp exclusively.

It will be extended in the future to do all
programming languages, but I intend to make
λ.el simple and effective for programming
in emacs lisp without bloat or over-complication.

Syntax forms

name type depends on basic idea
ieval MACRO ieval will imagine the evaluation of some code without any other context.
imacro/N MACRO imacro does not evaluate. It merely generates code, but that code is imagined. Like idefun it is variadic.
idefun FUNCTION ieval and imacro Run an imagined function on the given arguments and return an imagined result, but create a binding for the function.
ilist FUNCTION Generate a list of things. Return a real list.
ilambda / λ FUNCTION ieval Imaginarily run an expression on the given arguments and return an imagined result.
ifilter FUNCTION Imaginarily filter a real list with natural language and return a real list. Optionally, enforce cardinality.
iparse MACRO Given a syntax form / expression, will parse a syntax form with natural language. Returns the subform.
defimacro MACRO imacro/N Select the appropriate imacro/N form depending on the arity of arguments.

ieval

ieval will simply evaluate the provided
string/sexp as emacs lisp code. You
must provide ieval with, firstly, the preceding
code, which may be, for example, a function
definition or package requires, etc. and,
secondly, evaluated expression. Either
argument can either be a raw string containing
code or a sexp, but the expression will be
“one-line-ized” for the prompt.

ieval is used by idefun and ilambda.

prompt function for running the eval.

pf-imagine-evaluating-emacs-lisp/2
http://github.com/semiosis/prompts/blob/master/prompts/imagine-evaluating-emacs-lisp-2.prompt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
task: "imagine evaluating emacs lisp"
doc: "Given some elisp return the imagined result"
prompt-version: 1
prompt: |+
  <code>
  (message (eval <expression>))
  -->  
engine: "OpenAI Codex"
temperature: 0.2
max-generated-tokens: 60
top-p: 1.0
cache: on
stop-sequences:
- "n"
vars:
- "code"
- "expression"
validator: "grep -qv '(:return'"
examples:
- |-
    (defun double-number (x)
      (x * x))    
- "(double-number 5)"
filter: on
completion: off
insertion: off

The following is the implementation of ieval.

You may pass either a sexp or a raw string containing code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(defmacro ieval (expression &optional code)
  "Imaginarily evaluate the expression, given the code and return a real result."
  (let* ((code-str
          (cond
           ((stringp code) code)
           ((listp code) (pps code))))
         (expression-str
          (cond
           ((stringp expression) expression)
           ((listp expression) (pp-oneline expression))))
         (result (car
                  (pen-single-generation
                   (pf-imagine-evaluating-emacs-lisp/2
                    code-str expression-str
                    :no-select-result t :select-only-match t)))))
    (ignore-errors
      (eval-string result))))
1
2
3
4
(defun test-ieval-2 ()
  (ieval
   (double-number 5)
   "(defun double-number (x)n     (x * x))n;;Test the double-number function"))

Resulting prompt from test-ieval-2:

1
2
3
4
5
(defun double-number (x)
     (x * x))
;;Test the double-number function
(message (eval (double-number 5)))
--> <END>

ieval not only evaluates correctly despite
the deliberately incorrect naming of the
function (it multiplies rather than doubles),
but it returns the value as the correct data type.

1
2
3
4
5
(defun test-ieval ()
  (ieval
   (double-number 5)
   (defun double-number (x)
     (x * x))))

Expansion of test-ieval.

1
2
3
4
5
6
7
(let ((result
       (ieval
        (defun double-number (x)
          (x * x))
        (double-number 5))))
  (list2str (list result
                  (type result))))

ilambda / λ

Imaginarily run an expression on the given
arguments and return an imagined result.

Here are three ilambda subforms which take different arguments.

ilambda/task is the most terse. Only a NL
task description is given.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(defmacro ilambda/task (args task &optional name-sym)
  (let* ((slug (s-replace-regexp "-$" "" (slugify (eval task))))
         (fsym (or name-sym
                   (intern slug))))
    `(lambda ,args
       (eval
        ;; imagined by an LM
        `(ieval
          ;; An function and a function call
          (,',fsym ,,@args)
          ,,(concat ";; " task))))))
(defalias 'λ/task 'ilambda/task)

(defmacro ilambda/task-code (args task code &optional name-sym)
  (let* ((slug (s-replace-regexp "-$" "" (slugify (eval task))))
         (fsym (or
                name-sym
                (intern slug))))
    `(lambda ,args
       (eval
        ;; imagined by an LM
        `(ieval
          ;; An function and a function call
          (,',fsym ,,@args)
          (defun ,',fsym ,',args
            ,,task
            ,',code))))))
(defalias 'λ/task-code 'ilambda/task-code)

(defmacro ilambda/code (args code &optional name-sym)
  (let ((fsym (or name-sym
                  'main)))
    `(lambda ,args
       (eval
        ;; imagined by an LM
        `(ieval
          ;; An function and a function call
          (,',fsym ,,@args)
          (defun ,',fsym (,',@args)
            ,',code))))))
(defalias 'λ/code 'ilambda/code)

Demonstrations

1
2
(mapcar (ilambda/task (x) "double it")
        '(12 4))
1
2
3
(mapcar (ilambda/code (x)
                      (+ x 5))
        '(4))
1
2
3
4
(mapcar (ilambda/task-code (x)
                           "add five"
                           (+ x 5))
        '(8))

The ilambda macro.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(defmacro ilambda (args code-or-task &optional task-or-code name-sym)
  "Define an imaginary lambda (λ)"
  (let ((task (if (stringp code-or-task)
                  code-or-task
                task-or-code))
        (code (if (listp code-or-task)
                  code-or-task
                task-or-code)))
    (cond
     ((and code
           (sor task))
      `(ilambda/task-code ,args ,task ,code ,name-sym))
     ((sor task)
      `(ilambda/task ,args ,task ,name-sym))
     ((listp code-or-task)
      `(ilambda/code ,args ,code ,name-sym)))))

(defalias  'ilambda)
1
(-reduce (λ (x y) "add x to y") (number-sequence 1 3))

idefun

The idefun creates a binding to an imaginary
function. The implementation of the idefun
need not be specified in order for code to
run.

The new prompt function returned by idefun is provided with arguments and the
values of those arguments are taken and placed
into a prompt. An implementation may be
provided to idefun when defining the prompt function or optionally left out.
Unlike an imacro, when the prompt function
is evaluated the code is not returned. Rather,
the code is evaluted in imaginary space.

In short, the LM will imagine the evaluation
of the function as opposed to generate code.

idefun returns a binding to a new prompt
function.

Some examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(idefun add-two-numbers)
(add-two-numbers 5 8)

(idefun add-two-numbers (a b))
(add-two-numbers 5 8)

(idefun add-two-numbers (a b) "add a to b")
(add-two-numbers 5 8)

(idefun sum-of-integers)
(sum-of-integers 1 2 3 10 200 3000)

(idefun thing-to-hex-color)

(idefun add-two-numbers (a b) "add a to b")
1
2
3
(idefun generate-fib-sequence (n))

(pp-to-string (generate-fib-sequence 5))

With a temperature of 0.0, this will hash to
the same thing every time!

Strangely, we can’t call it a ’neural hash’ though.

1
2
3
4
5
(idefun sha-hash-string (s))

(pen-force
 ((temperature 0.0))
 (sha-hash-string "sugar shane"))
1
f1d3ff8ec24e91b957c9e55adec407f47b55e3ae
1
2
3
4
5
6
(idefun neural-hash-string (s)
  "This calculates a neural hash of the string.")

(pen-force
 ((temperature 0.0))
 (neural-hash-string "sugar shane"))

“0x7f8b8f8e”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(defmacro idefun (name-sym args &optional code-or-task task-or-code)
  "Define an imaginary function"
  (cond
   ((and (stringp name-sym)
         (not code-or-task))
    (progn
      (setq code-or-task name-sym)
      (setq name-sym (intern (s-replace-regexp "-$" "" (slugify (str name-sym)))))))
   ((and (symbolp name-sym)
         (not code-or-task))
    (setq code-or-task (pen-snc "unsnakecase" (sym2str name-sym)))))
  `(defalias ',name-sym
     (function ,(eval
                 `(ilambda ,args ,code-or-task ,task-or-code ,name-sym)))))

(idefun idoubleit (x)
        "double it")
1
2
3
4
5
6
(idefun distance-between-planets (x y)
        "distance between planets in astronomical units (AU)")

(concat (str (distance-between-planets "saturn" "jupiter"))
        "n"
        (str (distance-between-planets "mercury" "jupiter")))

I have no idea if this data is correct but it
seems consistent with itself.

1
2
3
4
5
6
(idefun distance-between-planets (x y)
        "distance between planets in million miles")

(concat (str (distance-between-planets "saturn" "jupiter"))
        "n"
        (str (distance-between-planets "mercury" "jupiter")))

Sadly, Codex doesn’t know too much about Scoville food hotness.

food Scoville scale
Pure capsaicin 16,000,000 SHU
Jalapeño 10,000 SHU
1
2
3
4
5
6
(idefun scoville-difference (food-a food-b)
        "difference between two foods in scoville Heat Units (SHUs)")

(concat (str (scoville-difference "Pure capsaicin" "Jalapeño"))
        "n"
        (str (scoville-difference "Chipotle" "Trinidad Scorpion Butch")))
1
2
3
(idefun hex-for-colour (colour))

(hex-for-colour "watermelon")
1
2
3
(idefun hex-for-colour (colour))

(hex-for-colour "snow")

Demo



imacro

An imacro actually imagines the
implementation of a function.

Components of the imacro should be inferred.
An imacro with only a function name should
work.

Also, an imacro is under the hood a regular
macro. This means, that expanding the imacro
will infer/generate underlying code.


1
2
3
(defun test-imacro-1 ()
  (interactive)
  (imacro/1 get-real-component-from-imaginary-number))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(defun test-imacro-1 ()
  (interactive)
  (progn
    (defun get-real-component-from-imaginary-number
        (number)
      "Return the real component of a complex number."
      (if
          (numberp number)
          (if
              (eq
               (car number)
               'polar)
              (car
               (cdr number))
            number)
        (error "Not a complex number: %s" number)))))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(defun test-imacro-1 ()
  (interactive)
  (progn
    (defun get-real-component-from-imaginary-number
        (number)
      "Return the real component of the complex number NUMBER."
      (if
          (numberp number)
          (if
              (eq
               (car number)
               'polar)
              (nth 1 number)
            number)
        number))))

pf-imagine-an-emacs-function/3
http://github.com/semiosis/prompts/blob/master/prompts/imagine-an-emacs-function-3.prompt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
title: imagine an emacs function
task: "imagine an emacs lisp function given name, arguments and docstring"
doc: "Given a function name, arguments and docstring, return the imagined body of the function"
prompt-version: 1
prompt: |+
  ;;my-emacs-library.el

  (defun <name> (<arguments>)
    "<docstring>"  
engine: "OpenAI Codex"
temperature: 0.2
max-generated-tokens: 1000
top-p: 1.0
cache: on
stop-sequences:
- "nn"
vars:
- "name"
- "arguments"
- "docstring"
validator: "chomp | sed -z 's/.*\(.\)$/\1/' | grep -q ')'"
examples:
- "times"
- "x y"
- "multiply two numbers and return a number"
preprocessors:
- "slugify"
postprocessor: chomp
postpostprocessor: "sed -z "s/^;;my-emacs-library.el\\n\\n//""
filter: on
completion: off
insertion: off
1
2
3
4
5
6
7
8
(car
 (pen-single-generation
  (pf-imagine-an-emacs-function/3
   "times"
   "x y"
   "multiply two numbers and return a number"
   :include-prompt t
   :no-select-result t)))
1
2
3
(defun times (x y)
  "multiply two numbers and return a number"
  (* x y))

There are 3 different versions of imacro
depending on how many arguments are supplied to
it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(defmacro imacro/3 (name args docstr)
  "Does not evaluate. It merely generates code."
  (let* ((argstr (apply 'cmd (mapcar 'slugify (mapcar 'str args))))
         (bodystr
          (car
           (pen-single-generation
            (pf-imagine-an-emacs-function/3
             name
             argstr
             docstr
             :include-prompt t
             :no-select-result t))))
         (body (eval-string (concat "'" bodystr))))
    `(progn ,body)))

(defmacro imacro/2 (name args)
  "Does not evaluate. It merely generates code."
  (let* ((argstr (apply 'cmd (mapcar 'slugify (mapcar 'str args))))
         (bodystr
          (car
           (pen-single-generation
            (pf-imagine-an-emacs-function/2
             name
             argstr
             :include-prompt t
             :no-select-result t))))
         (body (eval-string (concat "'" bodystr))))
    `(progn ,body)))

(defmacro imacro/1 (name)
  "Does not evaluate. It merely generates code."
  (let* ((bodystr
          (car
           (pen-single-generation
            (pf-imagine-an-emacs-function/1
             name
             :include-prompt t
             :no-select-result t))))
         (body (eval-string (concat "'" bodystr))))
    `(progn ,body)))
1
(imacro/3 my/itimes (a b c) "multiply three complex numbers")
1
2
3
4
5
(progn
  (defun my-times
      (x y z)
    "multiply three numbers and return a number"
    (* x y z)))

imacro expansion demo



1
(imacro/2 my/subtract (a b c))
1
2
3
4
5
6
7
8
(progn
  (defun my-subtract
      (a b c)
    "Subtract B from A and return the result."
    (setq result
          (+ a
             (- b c)))
    result))
1
2
3
4
5
(progn
  (defun my-subtract
      (a b)
    "Subtract A - B."
    (- a b)))

defimacro

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(defmacro defimacro (name &rest body)
  "Define imacro"
  (cond
   ((= 0 (length body))
    `(imacro/1
      ,name))
   ((= 1 (length body))
    `(imacro/2
      ,name
      ,(car body)))
   ((= 2 (length body))
    `(imacro/3
      ,name
      ,(car body)
      ,(cadr body)))))

All of the following are valid ways to invoke defimacro.

defimacro selects the right imacro/N function depending on the arity of the arguments.

1
2
3
4
(defimacro my/subtract)
(defimacro my/subtract (a b c))
(defimacro my/itimes (a b c)
   "multiply three complex numbers")



ilist

The easiest of the list of syntax forms I
aimed to implement, ilist simply takes a the
number of items to generate (n) and a string
describing the type of thing to generate
(type-of-thing). It will return a real list
of such things.

1
2
3
4
5
6
7
8
(defun ilist (n type-of-thing)
  (interactive (list (read-string-hist "ilist n: ")
                     (read-string-hist "ilist type-of-thing: ")))
  (pen-single-generation (pf-list-of/2 (str n) (str type-of-thing) :no-select-result t)))

(defun test-ilist ()
  (interactive)
  (etv (pps (ilist 10 "tennis players"))))

ifilter

ifilter takes

Example:

1
(pps (ifilter "is male" (ilist 10 "tennis players")))
1
(pps (ifilter (lambda (e) (full-name-p e)) (ilist 10 "tennis players")))

itest

1
2
3
4
5
6
7
8
9
(defmacro itest/m (predicate value)
  `(ieval
    `(my/test ,',value)
    `(defun my/test (x)
       (apply ,',predicate
              x))))

(defun itest (predicate value)
  (eval `(itest/m ,predicate ,value)))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(defun test-itest-1 ()
  (interactive)
  (etv
   (itest '(lambda (l) '(= 5 (length l)))
          '(a b c d))))

(defun test-itest-2 ()
  (interactive)
  (etv
   (itest/m (lambda (l) '(= 4 (length l)))
            '(a b c d))))

Derived functions

iparse

itransform

1
2
3
4
(defun get-backstory ()

  )
(itransform)
1
(pps (mapcar 'get-backstory (ilist 10 "tennis players"))

TODO Pure imaginary syntax forms

name
imaginary comparator


Source link