Example of coding in Scheme with AI agentic

Hi,

i present a transcript of an AI session for coding in Scheme. It is amazing how the AI agent has understood my code and what to do, and even know what rest to do that i had not asked yet.

Session Transcript — Adding $bracket-apply-string$

User

Modify the code to provide a $bracket-apply-string$ procedure on the model of $bracket-apply-vector$ from file bracket-apply.rkt. The $bracket-apply-string$ can be build from the general $bracket-apply$ procedure and the general apply-square-brackets-argument-* procedures. Some specialized apply-square-brackets-argument-*-string procedures must be written from the general ones named apply-square-brackets-argument*. The source code is in the directories /home/mattei/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket and /home/mattei/Dropbox/git/SRFI-105-for-Racket/bootstrapping.

Assistant

1. Reading the reference implementation

Read bracket-apply.rkt and parse-square-brackets.rkt in full to understand:

  • the generic $bracket-apply$ macro and its apply-square-brackets-argument-{0..5,6-and-more} procedures, which branch on vector?, hash-table?, string?, flomat?, array?, and an overload fallback;
  • the specialized $bracket-apply-vector$ macro and its apply-square-brackets-argument-{0..5,6-and-more}-vector procedures, extracted from the vector? branches of the generic ones;
  • the specialized $bracket-apply-flomat$ macro built the same way from the flomat? branches.

The plan: build $bracket-apply-string$ and apply-square-brackets-argument-*-string the same way, extracting the string? branches of the generic procedures.

2. bracket-apply.rkt — export the new macro

   (provide $bracket-apply$
	   apply-square-brackets-argument-1
           $bracket-apply-vector$
+	   $bracket-apply-string$
	   $bracket-apply-flomat$
	   )

3. bracket-apply.rkt — new string section (inserted between the vector section and the flomat section)

; string procedures and macros

(define-syntax $bracket-apply-string$  ;;  this implements a specialized bracket-apply for strings

  (lambda (stx)

    (syntax-case stx ()

      ;; a version that pre-compil the infix expression, should be faster
      (($bracket-apply-string$ container arg-bracket ...) ;  . args-brackets


       (with-syntax ((parsed-args

		      (cons #'list

                            (parse-square-brackets-arguments-lister-syntax (syntax->list #'(arg-bracket ...))
                                                                           (list (cons '* #'orig-*)
                                                                                 (cons '- #'orig--)
                                                                                 (cons '+ #'orig-+)
                                                                                 (cons '/ #'orig-/))))))

	 ;; when trying to put this 'case in a subroutine Racket complains of #'parsed-args being outside templates.
	 (case (length
		(cdr
		 (syntax->datum #'parsed-args))) ; we remove 'list from the length of syntax list (list arg1 arg2 ...) to get (arg1 arg2 ...)

	   ((0) #'(apply-square-brackets-argument-0-string container))

	   ;; 1 argument in [ ]
	   ;; T[index]
	   ((1) #'(apply-square-brackets-argument-1-string container
                                                           (first parsed-args)))
	   ;; 2 arguments in [ ]
	   ;; ex: T[i1 :] , T[: i2], T[i1 i2] , T[: :]

	   ;; {"hello"[2 :]}
	   ;; "llo"
	   ((2) #'(apply-square-brackets-argument-2-string container
                                                           (first parsed-args)
                                                           (second parsed-args)))

	   ;; 3 arguments in [ ]
	   ;; T[i1 : i2] , T[: : s]
	   ((3) #'(apply-square-brackets-argument-3-string container
                                                           (first parsed-args)
                                                           (second parsed-args)
                                                           (third parsed-args)))

	   ;; 4 arguments in [ ]
	   ;; T[: i2 : s] , T[i1 : : s] , T[i1 : i3 :]
	   ((4) #'(apply-square-brackets-argument-4-string container
                                                           (first parsed-args)
                                                           (second parsed-args)
                                                           (third parsed-args)
                                                           (fourth parsed-args)))

	   ;; 5 arguments in [ ]
	   ;; T[i1 : i3 : s]
	   ((5) #'(apply-square-brackets-argument-5-string container
                                                           (first parsed-args)
                                                           (second parsed-args)
                                                           (third parsed-args)
                                                           (fourth parsed-args)
                                                           (fifth parsed-args)))
	   ;; more than 5 arguments in [ ] : not allowed with strings
	   (else #'(apply-square-brackets-argument-6-and-more-string container parsed-args))))))))



(define (apply-square-brackets-argument-0-string container-eval)

  (string-copy container-eval)) ;; return a copy of the string


;; {T[:]}
;; "abc"
(define (apply-square-brackets-argument-1-string container-eval index-eval)

  ;; {"toto"[2]}
  ;; #\t
  ;; {"toto"[-1]}
  ;; #\o
  (if (equal? slice index-eval) ;; T[:]
      (string-copy container-eval) ;; return a copy of the string
      (if (< index-eval 0) ;; negative index as in Python
          (string-ref container-eval (+ (string-length container-eval) index-eval)) ;; negative indexing
          (string-ref container-eval index-eval)))) ;; return an element of the string


;; {"hello"[: 2]}
;; "he"
;; {"hello"[3 :]}
;; "lo"
;; {"hello"[: :]}
;; "hello"
(define (apply-square-brackets-argument-2-string container-eval index1-or-keyword-eval index2-or-keyword-eval)

  (cond ((and (equal? slice index1-or-keyword-eval) ;; T[: :]
	      (equal? slice index2-or-keyword-eval))
	 container-eval)

	((equal? slice index1-or-keyword-eval) ;; T[: i2]
	 (if (< index2-or-keyword-eval 0) ;; negative index
	     (substring container-eval 0 (+ (string-length container-eval) index2-or-keyword-eval))
	     (substring container-eval 0 index2-or-keyword-eval)))

	((equal? slice index2-or-keyword-eval) ;; T[i1 :]
	 (if (< index1-or-keyword-eval 0) ;; negative index
	     (substring container-eval (+ (string-length container-eval) index1-or-keyword-eval))
	     (substring container-eval index1-or-keyword-eval)))

	(else ;; syntax error
	 (error "apply-square-brackets-argument-2-string : bad arguments in string case,expecting : i2 or i1 :, provided :"
		index1-or-keyword-eval index2-or-keyword-eval))))


;; {"elephant"[2 : 5]}
;; "eph"
;;  {"abcdefghijkl"[: : 2]}
;; "acegik"
;; {"abcdefghijkl"[: : -3]}
;; "lifc"
;; {"123456789"[ :  : -1]}
;; "987654321"
(define (apply-square-brackets-argument-3-string container-eval index1-or-keyword-eval index2-or-keyword-eval index3-or-keyword-or-step-eval)

  (cond ((and (equal? slice index1-or-keyword-eval) ;; T[: : step]
	      (equal? slice index2-or-keyword-eval))

	 (when (= 0 index3-or-keyword-or-step-eval)
	       (error "apply-square-brackets-argument-3-string : slice step cannot be zero"))

	 (let* ((size-input (string-length container-eval))
		(size (quotient size-input (abs index3-or-keyword-or-step-eval)))
		(result '())
		(i 0))

	   (when (not (= (modulo size-input index3-or-keyword-or-step-eval) 0))
		 (set! size (+ 1 size)))

	   (set! result (make-string size))

	   (if (< index3-or-keyword-or-step-eval 0) ;; with negative index we start at end of vector (like in Python)
	       (for ((define k (- size-input 1)) (>= k 0) (set! k (+ k index3-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i)))
	       (for ((define k 0) (< k size-input) (set! k (+ k index3-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i))))

	   result))

	((equal? slice index2-or-keyword-eval) ;; T[i1 : i3]

	 (when (< index1-or-keyword-eval 0) ;; negative index
	       (set! index1-or-keyword-eval (+ (string-length container-eval) index1-or-keyword-eval)))

	 (when (< index3-or-keyword-or-step-eval 0) ;; negative index
	       (set! index3-or-keyword-or-step-eval (+ (string-length container-eval) index3-or-keyword-or-step-eval)))

	 (substring container-eval index1-or-keyword-eval index3-or-keyword-or-step-eval))



	((equal? slice index2-or-keyword-eval) ;; T[i1 : :]

	 (when (< index1-or-keyword-eval 0) ;; negative index
	       (set! index1-or-keyword-eval (+ (string-length container-eval) index1-or-keyword-eval)))

	 (string-copy container-eval index1-or-keyword-eval))


	((and (equal? slice index1-or-keyword-eval)  (equal? slice index3-or-keyword-or-step-eval)) ;; T[: i2 :]

	 (when (< index2-or-keyword-eval 0) ;; negative index
	       (set! index2-or-keyword-eval (+ (string-length container-eval) index2-or-keyword-eval)))

	 (string-copy container-eval 0 index3-or-keyword-or-step-eval))



	(else
	 (error "apply-square-brackets-argument-3-string : in string case, provided too much arguments:"
		index1-or-keyword-eval index2-or-keyword-eval index3-or-keyword-or-step-eval))))


;; {"abcdefghijklmno"[: 7 : 2]}
;; "aceg"
;; {"123456789"[: -3 : -2]}
;; "642"
(define (apply-square-brackets-argument-4-string container-eval index1-or-keyword-eval index2-or-keyword-eval index3-or-keyword-eval index4-or-keyword-or-step-eval)

  (cond ((and (equal? slice index1-or-keyword-eval) ;; T[: i2 : s]
	      (equal? slice index3-or-keyword-eval))

	 (when (= 0 index4-or-keyword-or-step-eval)
	       (error "apply-square-brackets-argument-4-string : slice step cannot be zero"))

	 (let* ((size 0)
		(result '())
		(i 0)
		(i2 index2-or-keyword-eval))

	   (when (< i2 0) ;; negative index
		 (set! i2 (+ (string-length container-eval) i2)))

	   (set! size (quotient i2 (abs index4-or-keyword-or-step-eval)))

	   (when (not (= (modulo i2 index4-or-keyword-or-step-eval) 0))
		 (set! size (+ 1 size)))
	   (set! result (make-string size))

	   (if (< index4-or-keyword-or-step-eval 0)

	       (for ((define k (- i2 1)) (>= k 0) (set! k (+ k index4-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i)))

	       (for ((define k 0) (< k i2) (set! k (+ k index4-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i))))

	   result))


	;; {"abcdefghijklmno"[3 : : 2]}
	;; "dfhjln"
	;; {"123456789"[3 :  : 2]}
	;; "468"
	;; {"123456789"[3 :  : -2]}
	;; "42"
	;; {"123456789"[-3 :  : -2]}
	;; "7531"
	;; {"123456789"[-3 :  : 2]}
	;; "79"
	((and (equal? index2-or-keyword-eval slice)  ;; T[i1 : : s]
	      (equal? index3-or-keyword-eval slice))

	 (when (= 0 index4-or-keyword-or-step-eval)
	       (error "apply-square-brackets-argument-4-string : slice step cannot be zero"))

	 (let* ((size-container-eval (string-length container-eval))
		(i1 (if (< index1-or-keyword-eval 0) ;; negative index
			(+ size-container-eval index1-or-keyword-eval)
			index1-or-keyword-eval))
		(size-input (if (> index4-or-keyword-or-step-eval 0)
				(- size-container-eval i1)
				(+ i1 1)))
		(size (quotient size-input (abs index4-or-keyword-or-step-eval)))
		(result '())
		(i 0))

	   (when (not (= (modulo size-input index4-or-keyword-or-step-eval) 0))
		 (set! size (+ 1 size)))
	   (set! result (make-string size))

	   (if (< index4-or-keyword-or-step-eval 0)

	       (for ((define k i1) (>= k 0) (set! k (+ k index4-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i)))

	       (for ((define k i1) (< k size-container-eval) (set! k (+ k index4-or-keyword-or-step-eval)))
		    (string-set! result
				 i
				 (string-ref container-eval k))
		    (set! i (+ 1 i))))

	   result))


	((and (equal? slice index2-or-keyword-eval) ;; T[i1 : i3 :]
	      (equal? slice index4-or-keyword-or-step-eval))

	 (let ((i1  index1-or-keyword-eval)
	       (i3 index3-or-keyword-eval))
	   (when (< i1 0) ;; negative index
		 (set! i1 (+ (string-length container-eval) i1)))
	   (when (< i3 0) ;; negative index
		 (set! i3 (+ (string-length container-eval) i3)))
	   (substring container-eval i1 i3)))


	(else
	 (error "apply-square-brackets-argument-4-string : in string case, provided too much arguments:"
		index1-or-keyword-eval index2-or-keyword-eval index3-or-keyword-eval index4-or-keyword-or-step-eval))))


;; {"123456789"[5 : 2 : -1]}
;; "543"
;; {"0123456789"[5 :  : -1]}
;; "543210"
;; {"0123456789"[5 : 0 : -1]}
;; "54321"
(define (apply-square-brackets-argument-5-string container-eval index1-eval index2-or-keyword-eval index3-eval index4-or-keyword-eval index5-or-step-eval)

  (if (and (equal? index2-or-keyword-eval slice)  ;; T[i1 : i3 : s]
	   (equal? index4-or-keyword-eval slice))

      (begin

	(when (= 0 index5-or-step-eval)
	  (error "apply-square-brackets-argument-5-string : slice step cannot be zero"))

	(let* ((size-container-eval (string-length container-eval))

	       (i1 (if (< index1-eval 0) ;; negative index
		       (+ size-container-eval index1-eval)
		       index1-eval))

	       (i3 (if (< index3-eval 0) ;; negative index
		       (+ size-container-eval index3-eval)
		       index3-eval))

	       (size-input (if (> index5-or-step-eval 0)
			       (- i3 i1)
			       (- i1 i3)))

	       (size (quotient size-input (abs index5-or-step-eval))) ;; result size
	       (result '())
	       (i 0))

	  (when (not (= (modulo size-input index5-or-step-eval) 0))
	    (set! size (+ 1 size)))

	  (if (<= size 0)

	      (make-string 0)

	      (begin
		(set! result (make-string size))

		(if (< index5-or-step-eval 0)

		    (for ((define k i1) (> k i3) (set! k (+ k index5-or-step-eval))) ;; we do not include i1-th element
			 (string-set! result
				      i
				      (string-ref container-eval k))
			 (set! i (+ 1 i)))

		    (for ((define k i1) (< k i3) (set! k (+ k index5-or-step-eval)))
			 (string-set! result
				      i
				      (string-ref container-eval k))
			 (set! i (+ 1 i))))

		result))))

      ;; T[i1 i2 i3 i4 i5] not allowed with strings
      (error "apply-square-brackets-argument-5-string : in string case, provided too much arguments:" index1-eval index2-or-keyword-eval index3-eval index4-or-keyword-eval index5-or-step-eval)))


(define (apply-square-brackets-argument-6-and-more-string container-eval args)

  (error "apply-square-brackets-argument-6-and-more-string : string used with [ ] and more than 5 arguments"))

This block was inserted right before the pre-existing ; flomat procedures and macros section.

4. Scheme+.rkt — export wiring

    $nfx$ ; infix
    $bracket-apply$ ; [ ]
    $bracket-apply-vector$
-   $bracket-apply-flomat$ 
+   $bracket-apply-string$
+   $bracket-apply-flomat$

5. operators.rkt — predicate wiring

	   MULTIPLY-op?
	   BRACKET-APPLY-op?
	   BRACKET-APPLY-VECTOR-op?
+	   BRACKET-APPLY-STRING-op?
	   BRACKET-APPLY-FLOMAT-op?
	   VALUES-op?
	   isBRACKET-APPLY?
	   isBRACKET-APPLY-VECTOR?
+	   isBRACKET-APPLY-STRING?
	   isBRACKET-APPLY-FLOMAT?
	   isBRACKET-APPLY*?
 (define (BRACKET-APPLY-VECTOR-op? oper)
   (or (eqv? oper '$bracket-apply-vector$)
       (datum=? oper '$bracket-apply-vector$)))

+(define (BRACKET-APPLY-STRING-op? oper)
+  (or (eqv? oper '$bracket-apply-string$)
+      (datum=? oper '$bracket-apply-string$)))
+
 (define (BRACKET-APPLY-FLOMAT-op? oper)
 (define (isBRACKET-APPLY-VECTOR? expr)
   (and (pair? expr) (BRACKET-APPLY-VECTOR-op? (car expr))))

+(define (isBRACKET-APPLY-STRING? expr)
+  (and (pair? expr) (BRACKET-APPLY-STRING-op? (car expr))))
+
 (define (isBRACKET-APPLY-FLOMAT? expr)
   (and (pair? expr) (BRACKET-APPLY-FLOMAT-op? (car expr))))

 (define (isBRACKET-APPLY*? expr)
   (or (isBRACKET-APPLY? expr)
       (isBRACKET-APPLY-VECTOR? expr)
+      (isBRACKET-APPLY-STRING? expr)
       (isBRACKET-APPLY-FLOMAT? expr)))

6. SRFI-105-for-Racket/bootstrapping/annot+.rkt — type-driven dispatch

Located the place where the annotation pass rewrites a generic $bracket-apply$ call into a specialized one once the container's type is known (type vector? ..., type flomat? ...), and added the string? case:

                                  (cond ((eq? tip-a1 'vector?)
					(cons '$bracket-apply-vector$
                                              a1_N))
				       ((eq? tip-a1 'flomat?)
					(cons '$bracket-apply-flomat$
                                              a1_N))
+				       ((eq? tip-a1 'string?)
+					(cons '$bracket-apply-string$
+                                              a1_N))
                                        (else
					(cons fst
                                              a1_N))))

7. Compilation checks

$ raco make bracket-apply.rkt
(no output — success)

$ raco make Scheme+.rkt
(no output — success)

8. Functional smoke test

First attempt (scratchpad directory didn't exist yet):

$ racket /tmp/.../scratchpad/test-bas.rkt
open-input-file: cannot open module file
  ...
  system error: no such file or directory; rkt_err=3

Created the directory, then a first test calling $bracket-apply-string$ with plain integer arguments (no slice marker), which correctly raised the expected error for the 2-argument, non-slice case:

$ racket /tmp/.../scratchpad/test-bas.rkt
apply-square-brackets-argument-2-string : bad arguments in string case,expecting : i2 or i1 :, provided : 1 3
  context...:
   ...
e
hello

Wrote a corrected test using the real : slice token from Scheme+/slice:

#lang racket/base
(require (only-in Scheme+/bracket-apply $bracket-apply-string$)
         (only-in Scheme+/slice :))

(displayln ($bracket-apply-string$ "hello" 1))               ;; #\e
(displayln ($bracket-apply-string$ "hello"))                 ;; "hello" (copy)
(displayln ($bracket-apply-string$ "hello" 1 :))              ;; "ello"
(displayln ($bracket-apply-string$ "hello" : 3))              ;; "hel"
(displayln ($bracket-apply-string$ "hello" 1 : 3))            ;; "el"
(displayln ($bracket-apply-string$ "abcdefghijkl" : : 2))     ;; "acegik"
(displayln ($bracket-apply-string$ "abcdefghijkl" : 6 : 2))   ;; "ace"
(displayln ($bracket-apply-string$ "0123456789" 5 : : -1))    ;; "543210"
(displayln ($bracket-apply-string$ "0123456789" 5 : 2 : -1))  ;; "543"

Output:

e
hello
ello
hel
el
acegik
ace
543210
543

All results matched the documented slicing semantics already used by the generic apply-square-brackets-argument-* string branches.

Started a further check (raco make annot+.rkt in the SRFI-105 bootstrapping directory) to confirm the annot+.rkt edit compiles, but the command was interrupted by the user before completing.

Notes on scope not covered

assignment.rkt (handling the <- assignment form, e.g. {V[i] <- x}) still only special-cases $bracket-apply-vector$ and $bracket-apply-flomat$ explicitly on the left-hand side of an assignment. $bracket-apply-string$ was not wired into that dispatch — the task was specifically scoped to the general $bracket-apply$ + apply-square-brackets-argument-* model (the read/index side), not the assignment machinery. Wiring in-place string mutation via {S[i] <- #\c} through the specialized macro would be a natural follow-up.

And of course it works:

#lang reader curly-infix
(require Scheme+)

(type string? str) ; this is a type annotation
{str := "hello"}
{str[3 :]}

Parsed code for curly infix and type annotation:

(:= str "hello")
($bracket-apply-string$ str 3 :) ; the specialized $bracket-apply$ for string indexing

Result:

Welcome to DrRacket, version 9.2 [cs].
Language: Determine language from source [custom]; memory limit: 8192 MB.

Scheme+ v18.0 by Damien Mattei.

SRFI 105 Curly Infix v15.0 for Scheme+.

"lo" ; this is the result !!!

Best Regards,

Damien

note: the optimisation with type annotation and type inference will only be available in the next version of Scheme+/curly-infix