Implementing targets as separate components #11

Closed
opened 5 years ago by kdsch · 5 comments
kdsch commented 5 years ago (Migrated from github.com)

I'm learning Pollen to ditch Hugo and every static site generator that frustrates me. I am somewhat familiar with Scheme, but a newcomer to Racket, especially the module system.

I've done the fourth tutorial, on multiple output targets. I understand that pollen.rkt defines a module that resolves Pollen tags. The tutorial uses branching expressions within tag functions to implement different targets.

I want to make the implementation choice at the module level rather than in the tag functions. In other words: separate targets into different components. I think of a Pollen document as requiring an interface, with the current poly target controlling which implementation is used. However, I'm not skilled enough in Racket to do this.

I'm learning Pollen to ditch Hugo and every static site generator that frustrates me. I am somewhat familiar with Scheme, but a newcomer to Racket, especially the module system. I've done the fourth tutorial, on multiple output targets. I understand that `pollen.rkt` defines a module that resolves Pollen tags. The tutorial uses branching expressions within tag functions to implement different targets. I want to make the implementation choice at the module level rather than in the tag functions. In other words: separate targets into different components. I think of a Pollen document as requiring an interface, with the current poly target controlling which implementation is used. However, I'm not skilled enough in Racket to do this.
mbutterick commented 5 years ago (Migrated from github.com)

Here’s one way to do it, putting each set of format-specific tag functions in its own source file, and then stitching everything together with a couple macros.

Here’s another way, using classes and inheritance.

Some have suggested that units are a possibility, but I don’t recall seeing a working example.

[Here’s one way to do it](https://gist.github.com/mbutterick/caf88360fc0090305937ca54539054bc), putting each set of format-specific tag functions in its own source file, and then stitching everything together with a couple macros. [Here’s another way](https://gist.github.com/mbutterick/b169900fedf84810b6335365b89562e4), using classes and inheritance. Some have suggested that [units](https://docs.racket-lang.org/guide/units.html) are a possibility, but I don’t recall seeing a working example.
kdsch commented 5 years ago (Migrated from github.com)

Thanks @mbutterick!

Thanks @mbutterick!
otherjoel commented 5 years ago (Migrated from github.com)

For what it’s worth, some time ago I looked into using units for this purpose. The problems I bumped into were

  1. At the time I couldn’t determine how to conditionally call define-values/invoke-unit/infer since it is a top-level form [edit: maybe rather, module level?]. With a better understanding of macros, this may not be an obstacle anymore.

  2. More crucially, it doesn’t appear that (current-poly-target) will return anything but 'html at expansion time.

For what it’s worth, some time ago I looked into using units for this purpose. The problems I bumped into were 1. At the time I couldn’t determine how to conditionally call [`define-values/invoke-unit/infer`][1] since it is a top-level form [edit: maybe rather, module level?]. With a better understanding of macros, this may not be an obstacle anymore. 2. More crucially, it doesn’t appear that `(current-poly-target)` will return anything but `'html` at expansion time. [1]: https://docs.racket-lang.org/reference/linkinference.html#%28form._%28%28lib._racket%2Funit..rkt%29._define-values%2Finvoke-unit%2Finfer%29%29
kdsch commented 5 years ago (Migrated from github.com)

@otherjoel Good to know.

I tried using units today. I found that define-values/invoke-unit/infer expects identifiers, which implies converting symbols (targets) into identifiers. This conversion would have to happen at run time, as the target varies at run time. I'm guessing this is a contradiction; I don't think identifiers can be created at run time. Whereas, macros can create them at compile time.

Recalling the notion of message passing I encountered in SICP, I experimented with that approach.

(define (target)
  (case (current-poly-target)
   [(html) html-target]
   [(txt)  txt-target]
   [else (error "unknown target:"
          (current-poly-target))]))

(define (heading . args) ((target) 'heading args))
(define (emph . args)    ((target) 'emph args))

(define (html-target method . args)
  (define (heading . elements)
    (txexpr 'h1 empty elements))

  (define (emph . elements)
    (txexpr 'strong empty elements))

  (case method
   [(heading) (heading args)]
   [(emph)    (emph args)]
   [else      (error "unknown method:" method)]))

This seems to have taken me a bit farther, but I'm now violating contracts in txexpr. Seems like a basic error, but I'm not sure what's going on, as the implementation didn't do this before.

txexpr: contract violation
  expected: txexpr-elements?
  given: '((("Karl Schultheisz")))

where resume.poly.pm contains

#lang pollen                                              

◊heading{Karl Schultheisz}                                

Today is ◊(get-date). I ◊emph{mostly} want this job.      
@otherjoel Good to know. I tried using units today. I found that `define-values/invoke-unit/infer` expects identifiers, which implies converting symbols (targets) into identifiers. This conversion would have to happen at run time, as the target varies at run time. I'm guessing this is a contradiction; I don't think identifiers can be created at run time. Whereas, macros can create them at compile time. Recalling the notion of message passing I [encountered in SICP](https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-22.html#%_sec_3.3.5), I experimented with that approach. ``` (define (target) (case (current-poly-target) [(html) html-target] [(txt) txt-target] [else (error "unknown target:" (current-poly-target))])) (define (heading . args) ((target) 'heading args)) (define (emph . args) ((target) 'emph args)) (define (html-target method . args) (define (heading . elements) (txexpr 'h1 empty elements)) (define (emph . elements) (txexpr 'strong empty elements)) (case method [(heading) (heading args)] [(emph) (emph args)] [else (error "unknown method:" method)])) ``` This seems to have taken me a bit farther, but I'm now violating contracts in `txexpr`. Seems like a basic error, but I'm not sure what's going on, as the implementation didn't do this before. ``` txexpr: contract violation expected: txexpr-elements? given: '((("Karl Schultheisz"))) ``` where `resume.poly.pm` contains ``` #lang pollen ◊heading{Karl Schultheisz} Today is ◊(get-date). I ◊emph{mostly} want this job. ```
kdsch commented 5 years ago (Migrated from github.com)

The problem was too many variadic functions. After fixing that, the message-passing style more or less does what I want.

The problem was too many variadic functions. After fixing that, the message-passing style more or less does what I want.
This repo is archived. You cannot comment on issues.
No Milestone
No project
No Assignees
1 Participants
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: mbutterick/pollen-users#11
Loading…
There is no content yet.