Use symbol _ or not if _ (underscore) is reserved already

hello,
i needed a single symbol to define a new syntax but almost all those that can be directly typed on an US keyboard are already used.
i wanted to use the underscore _ as a keyword (i think of / but my syntax can use numeric computation so no / is already used, i think of : but it is already used SRFI eager comprehension.
so i think now to use _ (underscore):

type or > _
_: wildcard not allowed as an expression in: _paste code here

it seems to be used in special case in scheme (Guile too...)

and i want to use it in this special syntax as a keyword:

> {T <+ (vector 1 2 3 5 6 7)}
'#(1 2 3 5 6 7)
> {T2 <+ T[2 _ 5]}
_: wildcard not allowed as an expression in: _

so i decided to do that, a bit ugly it is, i overwrite _ :

(define _ '())
(define slice _)

and now it works:

Welcome to DrRacket, version 8.6 [cs].
Language: reader "../Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/SRFI/SRFI-105-toplevel.rkt", with debugging; memory limit: 14000 MB.
> {T <+ (vector 1 2 3 5 6 7)}
'#(1 2 3 5 6 7)
> {T2 <+ T[2 _ 5]}
'#(3 5 6)

and we can still use _ as defined in Scheme language:

> (define xs (build-list 5 (λ _ (random))))
> xs
'(0.26274433048693946 0.6228901421560789 0.3809531045235335 0.4011282367712523 0.5968311471261267)
> 

the macro definition of bracket-apply from SRFI 151 that i develop is this portion ,used in this example for RHS (right expression):

(define-syntax $bracket-apply$
  
  (syntax-rules ()

 ;; 1 argument in []
    ;; T[index]
    ((_ container index)

..............................code cut ........................................


;; 3 arguments in []
    ;; T[i1 / i2] , T[i1 i2 i3] , T[/ / s]
    ((_ container index1-or-keyword index2-or-keyword index3-or-keyword-or-step)


     ($>

      ;; this portion evaluate the macro's arguments
      ;; to avoid compute it twice or many
      {container-eval <+ container}
      {index1-or-keyword-eval <+ index1-or-keyword}
      {index2-or-keyword-eval <+ index2-or-keyword}
      {index3-or-keyword-or-step-eval <+ index3-or-keyword-or-step}


     ;; {#(1 2 3 4 5 6 7)[2 / 5]}
     ;; '#(3 4 5)
     (cond ((vector? container-eval) ;; 3 dimension vector T[i1 i2 i3]? or T[i1 / i3] or T[/ / step] or  T[/ i2 /] or  T[i1 / /]

	    ;; {#(1 2 3 4 5 6 7 8)[/ / 3]}
	    ;; '#(1 4 7)

	    ;; {#(1 2 3 4 5 6 7 8)[/ / -2]}
	    ;; '#(8 6 4 2)
	    (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 "$bracket-apply$ : slice step cannot be zero"))
		   
		   (let* ((size-input (vector-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-vector 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)))
			      (vector-set! result
					   i
					   (vector-ref container-eval k))
			      (set! i (+ 1 i)))
			 (for ((define k 0) (< k size-input) (set! k (+ k index3-or-keyword-or-step-eval)))
			      (vector-set! result
					   i
					   (vector-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 (+ (vector-length container-eval) index1-or-keyword-eval)))
		   
		   (when (< index3-or-keyword-or-step-eval 0) ;; negative index
			 (set! index3-or-keyword-or-step-eval (+ (vector-length container-eval) index3-or-keyword-or-step-eval)))
		   
		   (vector-copy 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 (+ (vector-length container-eval) index1-or-keyword-eval)))
		 		 		   
		   (vector-copy container-eval index1-or-keyword-eval))
		  
		  .................. code cut ...................................;;

note: i no more use / that still is in comments, i use now _

in the LHS (left hand side) for assignation, example:

 ;; T[i1 / i2 / step]
> {s <+ (string-append "abcdefgh")}
"abcdefgh"
> {s[2 _ 7 _ 2] <- "0000"}
> s
"ab0d0f0h"
> (display s)
ab0d0f0h

the code use match pattern which require evaluated expressions, not symbols, here is a piece of the relevant code:

.................... header cut ........................;


;; scheme@(guile-user)> {T[3] <- T[2]}
;; $bracket-apply$
;; <- : vector or array set!
;; $4 = 4
;; scheme@(guile-user)> {T[3]}
;; $bracket-apply$
;; $5 = 4

;; scheme@(guile-user)> '{x <- y <- 7}
;; $1 = (<- x y 7)

;; $bracket-apply$ is from SRFI 105  bracket-apply is an argument of the macro
(define-syntax <-
  
  (syntax-rules ()

((_ (bracket-apply container index) expr)

............................code cut .......................;

;; this portion of Scheme+ is written in... Scheme+ !!!

;; 5 arguments in []
    ;; ex: T[i1 / i2 / step] , T[i1 i2 i3 i4 i5]
    ;;  {s[2 / 7 / 2] <- "000000000000"[3 / 7 / 1]} 
    ((_ (bracket-apply container index1 index2-or-keyword index3 index4-or-keyword index5-or-step) expr)

	($>

	  (unless (equal? (quote $bracket-apply$) (quote bracket-apply)) 
		  (error "Bad <- form: the LHS of expression must be an identifier or of the form ($bracket-apply$ array index1-or-keyword-or-step ...) , first argument "
			 (quote bracket-apply)
			 " is not $bracket-apply$."))

	  ;; this portion evaluate the macro's arguments
	  ;; to avoid compute it twice or many
	  {container-eval <+ container}
	  {index1-eval-pos <+ index1-eval <+ index1}
	  {index2-or-keyword-eval-pos <+ index2-or-keyword-eval <+ index2-or-keyword}
	  {index3-eval-pos <+ index3-eval <+ index3}
	  {index4-or-keyword-eval-pos <+ index4-or-keyword-eval <+ index4-or-keyword}
	  {index5-or-step-eval-pos <+ index5-or-step-eval <+ index5-or-step}
	  {expr-eval <+ expr}

	   (declare container-length container-copy!)
       
	   (if (vector? container)
	       ($
		{container-length <- vector-length}
		{container-copy! <- vector-copy!})
	       ($  ;; a string
		{container-length <- string-length}
		{container-copy! <- string-copy!}))
	   
	   ;; transform the negative indexes in positive ones when not slices
	   (when {(not (equal? index1-eval-pos slice)) and {index1-eval-pos < 0}}
		 {index1-eval-pos <- (container-length container-eval) + index1-eval-pos})
	   
	   (when {(not (equal? index2-or-keyword-eval-pos slice)) and {index2-or-keyword-eval-pos < 0}}
		 {index2-or-keyword-eval-pos <- (container-length container-eval) + index2-or-keyword-eval-pos})

	   (when {(not (equal? index3-eval-pos slice)) and {index3-eval-pos < 0}}
		 {index3-eval-pos <- (container-length container-eval) + index3-eval-pos})
	   
	   
	   (when {(not (equal? index4-or-keyword-eval-pos slice)) and {index4-or-keyword-eval-pos < 0}}
		 {index4-or-keyword-eval-pos <- (container-length container-eval) + index4-or-keyword-eval-pos})

	   (when {(not (equal? index5-or-step-eval-pos slice)) and {index5-or-step-eval-pos < 0}}
		 {index5-or-step-eval-pos <- (container-length container-eval) + index5-or-step-eval-pos})
      
              
       
	  (match (list index1-eval-pos index2-or-keyword-eval-pos index3-eval-pos index4-or-keyword-eval-pos index5-or-step-eval-pos)

		 ;; T[i1 / i2 / step]
		 ;;  {s <+ (string-append "abcdefgh")}
		 ;; "abcdefgh"
		 ;; > {s[2 / 7 / 2] <- "0000"}
		 ;; > s
		 ;; "ab0d0f0h"
		 ((list i1 (== slice) i2 (== slice) step-not-used)
		  
		  {step <+ index5-or-step-eval}

		  (copy-stepped-slice container-eval expr-eval i1 i2 step))
		  
		 
		 ;; T[i1 i2 i3 i4 i5]
		 ((list i1 i2 i3 i4 i5) 
		  
		  ;; normal case
		  (if (vector? container-eval)
			(function-array-n-dim-set! container-eval expr-eval (reverse (list i1 i2 i3 i4 i5))) ;;(array-n-dim-set! array expr-eval i1 i2)
			(array-set! container-eval index1-eval index2-or-keyword-eval index3-eval index4-or-keyword-eval index5-or-step-eval expr-eval)))

		 ) ;; match
	  ) ;; $>
	) ;; ((_ (bracket-apply container ...
    
.................................. code cut ...............................................

(define (copy-stepped-slice container-eval expr-eval i1 i2 step)
  
  (when {step = 0}
	(error "assignment : slice step cannot be zero"))

  {i <+ 0}

  (if {step < 0} ;; with negative index we start at end of vector (like in Python)
      (for ({k <+ i2} {k >= i1} {k <- k + step})
	   {container-eval[k] <- expr-eval[i]}
	   {i <- i + 1})
      
      (for ({k <+ i1} {k < i2} {k <- k + step})
	   {container-eval[k] <- expr-eval[i]}
	   {i <- i + 1})))

i'm asking if it could not break something in other scheme code to redefine _ ?
for now it seems no , i can use it in the macros,even in macros given in the above code that use _ as pattern and _ as a keyword inspired from : from Python.

Anyway i want a single character keyword like _ or : , : is used in Python ,example T[1:3]
but if someone have another idea?

regards,
Damien

There is a definition for _ already: 12.1 Pattern-Based Syntax Matching By writing (define _ '()), you are shadowing that definition. Because of Racket's phase system, this only has effect if your definition for _ is in the same phase as your use of syntax-case (or another macro that recognizes _): in Schemes that prohibit the same identifier from having different bindings in different phases, this will likely cause worse problems.

If you want to recognize the usual _ in a syntax-rules macro, list it as a literal:

(define-syntax demo
  (syntax-rules (_)
    [(_demo _)
     1]
    [(_demo other)
     other]))
(demo _)

More broadly, if the first thing a macro does is evaluate all of its subexpressions, that's a sign that it should probably expand to a function call to avoid copying a large block of code every time it is used.

It sounds like you want this to be portable to some other Schemes, but this is the sort of thing that would be much nicer to do with syntax-parse.

i agree in many points, syntax-parse would be better, macros are too big , i planned to write function for each 'cond case and 'match cases.

from your example i see you directly use _ in it , but in my code i define it in a variable named slice,i'm not sure it can be adapted easily.

i'm not sure to understan the meaning of phase in Racket , perheaps the same environment , when i made one more test (both in toplevel and module) where my redfined _ exist with the one defined in scheme , they don't seems to be in conflict:

(define _ '_)
(define slice _) ;; / , '..)

(define xs (build-list 5 (λ _ (random))))

it still give the good result for xs:
(0.3861121757215198 0.06575048451221101 0.36249401220091493 0.6169725878467547 0.37845392378941556)

Here's an example that, because of the definition for _, fails with the syntax error syntax-case: variable used twice in pattern in: _ instead of returning #t:

#lang racket
(define _ 'underscore)
(syntax-case #'(a b)  ()
  [(_ _)
   #t]
  [_
   #f])

I'd suggest this and the following section of The Racket Guide to start:

https://docs.racket-lang.org/guide/stx-phases.html

There's also some explanation in Greg Hendershott's Fear of Macros.

I would suggest not doing that. Instead, consider something like:

#lang racket
(define-syntax demo
  (syntax-rules (_)
    [(demo _ _ expr)
     (list '/ '/ expr)]
    [(demo e1 _ e2)
     (list e1 '/ e2)]))
(demo (+ 1 2) _ 3)

Unless you need your code to work on other Scheme systems, I'd really recommend something more like this:

#lang racket
(require (for-syntax syntax/parse))
(define-syntax (demo stx)
  (define-syntax-class expr-not-/
    (pattern e:expr
      #:fail-when (and (eq? '/ (syntax-e #'e)))
      "improper use of /"))
  (syntax-parse stx
    #:track-literals
    #:datum-literals (/)
    [(_ e1:expr-not-/ / e2:expr-not-/)
     #'(list e1 '/ e2)]
    [(_ / / e:expr-not-/)
     #'(list '/ '/ e)]))
(demo / / (+ 1 1))

thank you for your counter-example. It is clear i should use another symbol than _ for my slicing syntax.

i can not use | or # because they are used in SRFI 30: Nested Multi-line Comments

i think using

$

but i already use it in a macro which is a shorthand for begin and in a macro $> which is a shorthand for (let () body-including-definitions so i will use other symbols for my previous macros.

but for compatibilities and porting to other scheme implementation i will not use stx features.

it is really better with short macro cases,

i put all long macro bodies in function so the macro only expand in one or a few lines:

#lang reader "../SRFI/SRFI-105.rkt"

(require srfi/69) ;; Basic hash tables
(require srfi/25) ;; Multi-dimensional Array Primitives

(require (for-syntax r6rs/private/base-for-syntax)) ;; for macro syntax (for ... : identifier-syntax: undefined;

(provide $bracket-apply$)

(include "../included-files/array.scm")
(include "../library/for_next_step.scm")

(include "../included-files/def.scm")
(include "../included-files/block.scm")

(include "../included-files/bitwise.rkt")

(include "../included-files/slice.scm")

(define-syntax $bracket-apply$ ;;  this implements a possible $bracket-apply$ as proposed in SRFI-105
  
  (syntax-rules ()
    

    ;; 1 argument in []
    ;; T[index]
    ((_ container index)

     ;; this call evaluate the macro's arguments
     ;; this avoid compute it twice or many
     (apply-square-brackets-argument-1 container index))

    ;; note : example are given with  a slice keyword that could be different of the current one 

    ;; 2 arguments in []
    ;; ex: T[i1 /] , T[/ i2], T[i1 i2] , T[/ /]
    
    ;; {#(1 2 3 4 5)[inexact->exact(floor(2.7)) $]}
    ;; '#(3 4 5)
    ((_ container index1-or-keyword index2-or-keyword)

      (apply-square-brackets-argument-2 container index1-or-keyword index2-or-keyword))

    ;; 3 arguments in []
    ;; T[i1 / i2] , T[i1 i2 i3] , T[/ / s]
    ((_ container index1-or-keyword index2-or-keyword index3-or-keyword-or-step)

     (apply-square-brackets-argument-3 container index1-or-keyword index2-or-keyword index3-or-keyword-or-step))

    ;; 4 arguments in []
    ;; T[/ i2 / s] , T[i1 / / s] , T[i1 / i3 /] , T[i1 i2 i3 i4]
    ((_ container index1-or-keyword index2-or-keyword index3-or-keyword index4-or-keyword-or-step) 

     (apply-square-brackets-argument-4 container index1-or-keyword index2-or-keyword index3-or-keyword index4-or-keyword-or-step))


    ;; 5 arguments in []
    ;; T[i1 / i3 / s] , T[i1 i2 i3 i4 i5]
    ((_ container index1 index2-or-keyword index3 index4-or-keyword index5-or-step)

     (apply-square-brackets-argument-5 container index1 index2-or-keyword index3 index4-or-keyword index5-or-step)) 
 
    
    ;; more than 5 arguments in []
    ;; T[i1 i2 i3 i4 i5 ...]
    ((_ array index1 index2 index3 index4 index5 ...)


     ($+>
      
      ;; this portion evaluate the macro's arguments
      ;; to avoid compute it twice or many
      {array-eval <+ array}
      ;; note: we can not evaluate ellipsis before!
      
      (if (vector? array)
	  (function-array-n-dim-ref array-eval (reverse (list index1 index2 index3 index4 index5 ...))) ;;(array-n-dim-ref array index1 index2 ...)   ;; note: we can not evaluate ellipsis before!
	  (array-ref array-eval index1 index2 index3 index4 index5 ...))   ;; array SRFI 25

      ) ;; $+>

     ) ;;  ((_ array index1 ...
    ) ;; (syntax-rules ()

  ) ;; (define-syntax $bracket-apply$ ... 


;; {T[$]}
;; '#(1 2 3)
(define (apply-square-brackets-argument-1 container-eval index-eval)
  
.......................  code cut  ..................................

after are all apply-square-brackets-argument-* for various macro cases (based on number of args)

but again i will have to rewrite all that to provide infix with operator precedence directly between the [ ] .