How to access current output type from `pollen.rkt'?

Visto 82 veces
Saltar al primer mensaje no leído

lfac...@jhu.edu

no leída,
20 oct 2016, 11:33:4420/10/16
a Pollen
PRE-CONDITIONS

The book contains the files:


#lang pollen

◊foo{Bar}



#lang pollen

◊foo{Baz}


pollen.rkt

#lang racket

(define (foo . elements)
  ...)


USE CASE

The foo function needs to behave differently when generating different output types—namely, HTML and Atom. For example, suppose it generates a link: the link’s path can be relative when generating HTML, but it must be absolute when generating an Atom feed.


QUESTION

How do I fill in the ... in the pollen.rkt above to achieve this goal?


NON-ANSWERS
  1. Use different functions foo/html and foo/atom. This doesn’t work because some of the function calls are implicit. For example, in my real code, I want this level of control on root, which is a magical function called by Pollen on the module level.
  2. Use (current-poly-target). Notice that the files have extensions such as .html.pm and .atom.pm, and not .poly.pm. This is no accident, since each file only generates one output type. Thus, (current-poly-target) always answers with the default output type, 'html.
  3. Use (->output-path (select-from-metas 'here-path metas)).  The metas are not available to pollen.rkt. It could not be, since metas come from the source modules (for example, index.html.pm), but the source modules need the definitions in pollen.rkt. It would be a circular dependency.

BAD ANSWER

Use (current-poly-target) to fill in the ... and define foo in pollen.rkt. Overwrite the parameter in each source file (for example, feed.atom.pm) with the line ◊(current-poly-target 'atom). This works, but is not good, because it requires overwriting the parameter in a lot of files. It would be better if the overwrite worked on template files (for example, template.atom.p), but that is not the case.

———————————————————————————

Thank you all for your time.

Matthew Butterick

no leída,
20 oct 2016, 14:02:5620/10/16
a lfac...@jhu.edu,Pollen
On Oct 20, 2016, at 8:33 AM, lfac...@jhu.edu wrote:
  1. Use (current-poly-target). Notice that the files have extensions such as .html.pm and .atom.pm, and not .poly.pm. This is no accident, since each file only generates one output type. Thus, (current-poly-target) always answers with the default output type, 'html.

`current-poly-target` is a Racket parameter, which is a special data type that allows imperative action from afar. Rather than rely on Pollen inferring the poly target from the filename, you can set it directly:

;; pollen.rkt
#lang racket/base
(provide (all-defined-out))

(require pollen/setup)
(define (foo . xs)
  (case (current-poly-target)
    [(atom) 'do-atom-foo]
    [else 'do-html-foo]))


#lang pollen

;; we won't use the variable `target`, 
;; but if we set the parameter without assigning it,
;; it evaluates to `void`, which we'd have to filter out 
◊(define target (current-poly-target 'atom))

◊foo{bar}


> '(root do-atom-foo "\n")


  1. Use (->output-path (select-from-metas 'here-path metas)).  The metas are not available to pollen.rkt. It could not be, since metas come from the source modules (for example, index.html.pm), but the source modules need the definitions in pollen.rkt. It would be a circular dependency.

Ah, that is actually not true. The `doc` relies on "pollen.rkt", but the `metas` do not, because they live in an independent submodule. See [1]. This is deliberate, so you can fetch the metas quickly, without the overhead of running "pollen.rkt". The side effect is that there is no circular dependency.

But you still need to pass the metas to the `foo` function somehow. Of course, you can do this explicitly ...

◊foo2[metas]{bar}

... but it's sleeker to use a macro. Here's one way to do it:

;; pollen.rkt
#lang racket/base
(provide (all-defined-out))

(require (for-syntax racket/base) pollen/file sugar/file pollen/core)

;; if this macro doesn't make sense I will elaborate
(define-syntax (foo2 stx)
  (syntax-case stx ()
    [(_ ARG ...)
     (with-syntax ([METAS (datum->syntax stx 'metas)])
       #'(do-foo2 (select-from-metas 'here-path METAS) ARG ...))]))

(define (do-foo2 here-path . xs)
  (define ext (string->symbol (get-ext (->output-path here-path))))
  (case ext
    [(atom) 'do-atom-foo2]
    [else 'do-html-foo2]))

#lang pollen

◊foo2{bar}


> '(root do-atom-foo2 "\n")



  1. Use different functions foo/html and foo/atom. This doesn’t work because some of the function calls are implicit. For example, in my real code, I want this level of control on root, which is a magical function called by Pollen on the module level

You could make `root` into a macro, following the pattern shown above, and branch to `foo/html` and `foo/atom`.



lfac...@jhu.edu

no leída,
20 oct 2016, 14:50:1820/10/16
a Pollen,lfac...@jhu.edu
As usual, thank you very much. Your quick, precise and complete answers are part of what makes using Pollen a joy.

I think adding assignments to current-poly-target in all source files would pollute them. This is redundant information already stated in the file name. That is why I regarded it as a bad answer—even though it is a correct one—in my original post.

In light of your explainations, what I would really like is for (in order of preference):
  1. The existence of a current-output-type function, available to pollen.rkt. It would return the same things that current-poly-target returns, but would always be in correspondence with the output type in question, regardless of the source file being (for example) .html.pm or .poly.pm.
  2. When using a source file with an extension other than .poly.pm, the current-poly-target parameter is set to the default, which isn’t very helpful. So, in that case, current-poly-target could change to do what I described in the point above. Granted, current-poly-target is not as aptly named to that purpose as current-output-type, but it might fit better with the existing system and documentation.
  3. The existence of a metas variable, available to pollen.rkt. With it, I could create current-output-type, as you showed in your answer.
If you disagree with all these proposals, I guess I’m going to try the idea of using macros to transparently get a hold of metas in pollen.rkt, as you described.

Matthew Butterick

no leída,
20 oct 2016, 16:00:5520/10/16
a lfac...@jhu.edu,Pollen
On Oct 20, 2016, at 11:50 AM, lfac...@jhu.edu wrote:

  1. The existence of a current-output-type function, available to pollen.rkt. It would return the same things that current-poly-target returns, but would always be in correspondence with the output type in question, regardless of the source file being (for example) .html.pm or .poly.pm.
  2. When using a source file with an extension other than .poly.pm, the current-poly-target parameter is set to the default, which isn’t very helpful. So, in that case, current-poly-target could change to do what I described in the point above. Granted, current-poly-target is not as aptly named to that purpose as current-output-type, but it might fit better with the existing system and documentation.

`current-poly-target` is meant to fill in a detail that cannot be inferred from the source file name. 

How is your proposed `current-output-type` parameter different from an expression like this? (in pseudocode):

(if source-file-has-poly-ext
   (current-poly-target)
   (get-source-file-ext))


  1. The existence of a metas variable, available to pollen.rkt. With it, I could create current-output-type, as you showed in your answer.

I don't see how this would be better than just passing `metas` to functions in "pollen.rkt" when needed. In general, I'm wary of adding magic communication to the supported interface when standard Racket idiom will suffice. (Though I would not talk anyone out of structuring a project that way.)

The advantage of a macro in a case like this is that it's a little bit hacky, but the hackiness is contained. 



lfac...@jhu.edu

no leída,
20 oct 2016, 17:24:0820/10/16
a Pollen,lfac...@jhu.edu
How is your proposed `current-output-type` parameter different from an expression like this? (in pseudocode):

(if source-file-has-poly-ext
   (current-poly-target)
   (get-source-file-ext))

No difference at all, you precisely captured the intent of my proposal. My point is that, to actually write get-source-file-ext in pollen.rkt it’s necessary to have some form of feedback from the source file. Either by a Racket parameter (for example, current-poly-target) or by the function arguments (for example, by accepting the metas as the first parameter). I was hoping you’d agree it’d be nice to have a magical feedback channel provided by Pollen 😀
 
I don't see how this would be better than just passing `metas` to functions in "pollen.rkt" when needed. In general, I'm wary of adding magic communication to the supported interface when standard Racket idiom will suffice. (Though I would not talk anyone out of structuring a project that way.)

The advantage of a macro in a case like this is that it's a little bit hacky, but the hackiness is contained.

Sure, it’s a trade-off between convenience and simplicity. You are preferring simplicity, which is perfectly reasonable.

Thanks a lot for the conversation!
Responder a todos
Responder al autor
Reenviar
0 mensajes nuevos