Text Templates in Scheme

(import (rnrs)
        (rnrs io simple))

;; We can create a new template and parse its body from a string.
;; Templates are a mix of static text and "actions" enclosed in
;; {{...}} that are used to dynamically insert content.
(define (create-template template-string)
  (lambda (value)
    (let ((result ""))
      (let loop ((parts (string-split template-string "{{}}"))
                 (is-action #f))
        (if (null? parts)
            result
            (begin
              (if is-action
                  (set! result (string-append result (->string value)))
                  (set! result (string-append result (car parts))))
              (loop (cdr parts) (not is-action))))))))

;; Helper function to split a string
(define (string-split str delimiters)
  (let loop ((chars (string->list str))
             (current '())
             (result '()))
    (cond
     ((null? chars)
      (reverse (cons (list->string (reverse current)) result)))
     ((member (car chars) (string->list delimiters))
      (loop (cdr chars)
            '()
            (cons (list->string (reverse current))
                  (cons (string (car chars)) result))))
     (else
      (loop (cdr chars)
            (cons (car chars) current)
            result)))))

;; Create and use a simple template
(define t1 (create-template "Value is {{}}"))
(display (t1 "some text"))
(newline)
(display (t1 5))
(newline)
(display (t1 '("Scheme" "Lisp" "Racket" "Clojure")))
(newline)

;; Template for structures (in Scheme, we'll use association lists)
(define t2 (create-template "Name: {{}}"))
(display (t2 (cdr (assoc 'Name '((Name . "Jane Doe"))))))
(newline)
(display (t2 (cdr (assoc 'Name '((Name . "Mickey Mouse"))))))
(newline)

;; Conditional template (simplified)
(define (conditional-template value)
  (if (and (string? value) (not (string=? value "")))
      "yes"
      "no"))
(display (conditional-template "not empty"))
(newline)
(display (conditional-template ""))
(newline)

;; Range template (simplified for lists)
(define (range-template lst)
  (string-append "Range: "
                 (apply string-append
                        (map (lambda (x) (string-append x " "))
                             lst))))
(display (range-template '("Scheme" "Lisp" "Racket" "Clojure")))
(newline)

This Scheme code demonstrates concepts similar to text templates in the original example. Here’s a breakdown of the translation:

  1. We define a create-template function that takes a template string and returns a function. This function, when called with a value, replaces {{}} in the template with the provided value.

  2. The string-split helper function is used to split the template string into parts.

  3. We create and use simple templates similar to the original example, demonstrating string and number interpolation.

  4. For struct-like behavior, we use association lists in Scheme.

  5. The conditional template is simplified to a function that returns “yes” or “no” based on whether the input is a non-empty string.

  6. The range template is implemented as a function that takes a list and formats it similar to the original example.

Note that Scheme doesn’t have built-in template functionality like Go, so we’ve implemented a basic version to demonstrate the concept. This implementation is simplified and doesn’t cover all the features of Go’s text templates, but it provides a similar idea of dynamic content generation.

To run this Scheme code, you would save it to a file (e.g., templates.scm) and run it with a Scheme interpreter that supports R6RS, such as Chez Scheme:

$ chez --script templates.scm
Value is some text
Value is 5
Value is (Scheme Lisp Racket Clojure)
Name: Jane Doe
Name: Mickey Mouse
yes
no
Range: Scheme Lisp Racket Clojure 

This example demonstrates how to create and use simple text templates in Scheme, providing functionality similar to the original Go example while adapting to Scheme’s functional programming paradigm.