# Experiment with defines: define unity

**URL:** <https://racket.discourse.group/t/experiment-with-defines-define-unity/314>\
**Category:** Show & Tell\
**Tags:** feedback\
**Created:** [November 30, 2021, 2:38am UTC](https://racket.discourse.group/t/experiment-with-defines-define-unity/314 "2021-11-30T02:38:06Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [November 30, 2021, 2:38am UTC](https://racket.discourse.group/t/experiment-with-defines-define-unity/314/1 "2021-11-30T02:38:06Z")

</div>

I went through a phase (which may now be over) where I thought it was really odd that we had a whole bunch of `define`-like forms. (Why? Mostly for reasons of getting indentation right in vim: vim uses an option `lispwords` to know what forms to indent like `define` or `cond` (special forms) and what should indented like function application. Every new language or DSL's `define`-like form meant making sure `lispwords` has enough names. It's possible that `raco fmt` will fix this, but I haven't tried it yet. With the following idea, `lispwords` only needs `define` and the handful of forms like `cond`, `for/list`, etc.)

During that phase, I created the following possibly monstrous code, which I nicknamed "define unity" (obvious pun intended). The code redefines `define` to carry a notion of "transformer" (keyword argument) that controls how the `define` functions. It includes some sample uses.

There are obvious problems:

- no arrows from keywords, since they aren't identifiers with bindings. Could this even be solved? I only recently learned about `#:source` for `format-id`, maybe that will do it?
- I would like to write `(define #:values (x y z) (values 1 2 3))` and not `(define #:from-values (x y z) (values 1 2 3))`. Idem. for `match/define` and `#:match` vs. `#:from-match`.
- `(define #:list 1 2 3)` is syntactically correct, and is not a definition at all

OTOH, I imagined a world of `(require define/unity)` followed by a list of "define unity" libraries, providing the kind of syntax-transformers that cooperate well as keyword arguments to the new define (which, by the way, still also functions as a regular define). Patterns emerged, like using `(define #:k x v)` for defining values but `(define (f x) #:k b)` for functions and macros.

I'm sharing the code here for feedback on the idea, and in case anyone is interested.

```scheme
#lang racket

(require syntax/parse/define
         (only-in racket/base [define def])
         (for-syntax racket/syntax)
         rackunit)

(define-syntax define
  (syntax-parser
    [(_ {~or {~seq t:keyword head}
             {~seq head t:keyword}}
        . body)
     #:with transformer (format-id #'t "~a" (syntax->datum #'t))
     (syntax/loc this-syntax
       (transformer head . body))]
    [(_ head . body)
     (syntax/loc this-syntax
       (def head . body))]))

(define (rename-transformer name:id impl:id)
  #:define-syntax-parse-rule
  (define #:define-syntax name (make-rename-transformer #'impl)))

(define (strip-define sans-define:id)
  #:define-syntax-parse-rule
  #:with name (format-id #'sans-define "define-~a" #'sans-define)
  (define #:rename-transformer sans-define name))

(define #:strip-define syntax-parse-rule)
(define #:strip-define syntax)

(define #:rename-transformer from-match match-define)

;; {{{ define-steps impl
(module steps racket
  (provide define-steps)

  (require syntax/parse/define
           (for-syntax syntax/parse/lib/function-header))

  (define step-level (make-parameter -1))
  (define (indent) (build-string (* (step-level) 3) (λ _ #\space)))

  (begin-for-syntax
    (define-splicing-syntax-class step
      #:datum-literals (step)
      (pattern {~seq step message:string {~and form:expr {~not step}} ...})))

  (define-syntax-parse-rule (define-steps header:function-header step:step ...+)
    (define header
      (parameterize ([step-level (add1 (step-level))])
        (define step-number -1)
        (begin
          (set! step-number (add1 step-number))
          (displayln (format "~a~a. ~a" (indent) step-number step.message))
          step.form ...)
        ...)))) ;; }}}

(require 'steps)
(define #:rename-transformer with-steps define-steps)
(define #:strip-define steps)

;; --------------------------------------------------

(define foo 123)
(check-equal? foo 123)

(define (bar x) (list foo x))
(check-equal? (bar 456) (list 123 456))

(define (baz x)
  #:syntax-parse-rule
  (bar x))
(check-equal? (baz 456) (list 123 456))

(define #:from-match `(,a ,b, c) (list 1 2 3))
(check-equal? (list a b c) (list 1 2 3))

(define #:rename-transformer from-values define-values)
;; don't want to redefine values
;; suggests a change: transform #:values into values-define-transformer or
;; similar, so that we can use #:values, #:match, etc., without issue.
;; test for binding? preferred order?
;; (define #:strip-define values)

(define #:from-values (x y z) (values 1 2 3))
(check-equal? (list x y z) (list 1 2 3))

;; ??
(check-equal? (define #:list 1 2 3) (list 1 2 3))

(define (qux bar)
  #:steps
  step "Setup"
  (define a 1)
  (define b 2)
  step "Make a list"
  (list a b bar))

(define #:with-steps (zam zoink)
  step "My variables"
  (define c 3)
  step "Make a list with sublists"
  (list (qux c) zoink))

(check-equal? (with-output-to-string (thunk (check-equal? (qux 3) '(1 2 3))))
              "0. Setup\n1. Make a list\n")

(check-equal? (with-output-to-string (thunk (check-equal? (zam 3) '((1 2 3) 3))))
              #<<EOS
0. My variables
1. Make a list with sublists
   0. Setup
   1. Make a list

EOS
                )

```

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [May 10, 2022, 12:33am UTC](https://racket.discourse.group/t/experiment-with-defines-define-unity/314/2 "2022-05-10T00:33:33Z")

</div>

If I may steal @soegaard 's thunder, they recently posted [GitHub - soegaard/bind: Bind: Let with binding clause transformers](https://github.com/soegaard/bind) as "bindings galore" in Discord.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [May 10, 2022, 10:40am UTC](https://racket.discourse.group/t/experiment-with-defines-define-unity/314/3 "2022-05-10T10:40:02Z")

</div>

@stchang @AlexKnauth

Stephen Chang and Alexander Knauth has this related package:

[https://docs.racket-lang.org/generic-bind/index.html](https://docs.racket-lang.org/generic-bind/index.html)

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [May 10, 2022, 11:00am UTC](https://racket.discourse.group/t/experiment-with-defines-define-unity/314/4 "2022-05-10T11:00:41Z")

</div>

The documentation has moved:  
[https://soegaard.github.io/docs/bind/bind.html](https://soegaard.github.io/docs/bind/bind.html)
