Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this isn’t a “well, actually …” but rather ideas for further inquiry #1

Closed
mbutterick opened this Issue Aug 14, 2015 · 1 comment

Comments

Projects
None yet
2 participants
@mbutterick
Copy link

mbutterick commented Aug 14, 2015

First, it feels like you could combine the counting aspect of things with the label-gathering aspect. If every countable tag has a label (for identification purposes, right?) then it seems like you can gather those labels into a list. The length of that list is the count. (And it also tells you the order in which labels appeared).

Second, you probably haven’t delved into syntax transformation (aka macros) yet, but you might want to dip a toe, as it may be a more direct way of expressing these ideas.

For instance, here’s a quick example showing how you can dynamically create variables with a macro (in this case section-labels and subsection-labels:

"directory-require.rkt"

#lang racket
(require (for-syntax racket/syntax))
(provide (all-defined-out))

(define-syntax (define-countable-tag stx)
  (syntax-case stx ()
    [(_ tagname proc)
     (with-syntax ([tagname-labels (format-id stx "~a-labels" #'tagname)])
       #'(begin
           (define tagname-labels null)
           (define (tagname label . xs)
             (set! tagname-labels `(,@tagname-labels ,label))
             (apply proc xs))))]))

;; notice that section-labels and subsection-labels are not explicitly defined
(define-countable-tag section
  (λ xs `(section+ ,@xs)))

(define-countable-tag subsection
  (λ xs `(subsection+ ,@xs)))

"test.html.pm"

#lang pollen

◊section['intro]{Introduction}
◊section['next]{Next}

◊subsection['one]{This is one}
◊subsection['two]{This is two}

When you run this file, you’ll get this:

'(root (section+ "Introduction") "\n" "\n" (section+ "Next") "\n" "\n" (subsection+ "This is one") "\n" "\n" (subsection+ "This is two") "\n" "\n")

But then at the REPL:

> section-labels
'(intro next)
> subsection-labels
'(one two)

In this case, the label-gathering / counting happens as a side effect of the tag functions.

@malcolmstill

This comment has been minimized.

Copy link
Owner

malcolmstill commented Aug 15, 2015

I've written plenty macros in Common Lisp but I'm still trying to wrap my head around Racket macros, thanks for excuse to try and figure them out!

Yeah, the counting and labelling can occur, with a macro as you say above, before the root function is called. To count and cross-reference you need two passes, so this removes the first pass from having to be carried out in root.

I've been having a think and I've come up with the following macro. I don't think labelling should be mandatory: you should only have to label what you want to reference. I realised we can have the generated tag function take an optional keyword argument #:label.

(define-syntax (define-countable-tag stx)
  (syntax-case stx ()
    [(_ (tagname a ... . rest) (initial render parent-tagname separator) (count) proc ...)
     (with-syntax ([tag-counter (format-id stx "~a-counter" #'tagname)]
                   [parent-counter (if (symbol? (syntax->datum #'parent-tagname))
                                       (format-id stx "~a-counter" #'parent-tagname)
                                       #'parent-tagname)])
       #'(begin
           (define tag-counter (make-counter initial render parent-counter separator))
           (define (tagname #:label [label #f] a ... . rest)
             (tag-counter 'increment)
             (define count (tag-counter 'to-string))
             (if label
                 (begin
                   (update-label-map label count)
                   (attr-set proc ... 'id label))
                 proc ...))))]))

This allows for the following definition of a figure tag for example:

(define-countable-tag (figure src #:width [width "90%"] . xs) (0 number->string #f ".") (count)
    `(figure
      (img ((width ,width) (src ,src)))
      (figcaption ,count ": " ,@xs)))

Example usage in a REPL:

> (figure "a.png" "Image a")
'(figure
  (img ((width "90%") (src "a.png")))
  (figcaption "1" ": " "Image a"))
> (figure "b.png" #:label "fig:b" "Image b")
'(figure
  ((id "fig:b"))
  (img ((width "90%") (src "b.png")))
  (figcaption "2" ": " "Image b"))

Cool!

I've committed the changes.

One thing on my mind:

  • Should the macro set the id attribute to the label...is that general enough? Maybe it should set some other value (e.g. data-label), or the label should be available to the tag function in the same way that the current count is?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.