◊require in templates #65

Closed
opened 9 years ago by kirelagin · 3 comments
kirelagin commented 9 years ago (Migrated from github.com)

I’m stuck with a very dumb question. I can’t figure out how to require something in a template.

For example, I want to use xexpr->string instead of ->html, so I neeed a way to ◊(require xml), however:

(require xml)

(xexpr->string doc)

gives me:

template.md:1:1: require: not at module level or top level
  in: (require xml)
  context...:
   /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/racket/private/reqprov.rkt:243:2
   core225
   render-to-file
   unpack62
   loop
   (submod /home/kirrun/.racket/6.1.1/pkgs/pollen/command.rkt raco): [running body]
   /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/raco/raco.rkt: [running body]
   /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/raco/main.rkt: [running body]

In the future, I will want to use even more complex functions, potentially required from other packages. So what is the recommended way to do this?

I’m stuck with a very dumb question. I can’t figure out how to `require` something in a template. For example, I want to use `xexpr->string` instead of `->html`, so I neeed a way to `◊(require xml)`, however: ``` Racket ◊(require xml) ◊(xexpr->string doc) ``` gives me: ``` Console template.md:1:1: require: not at module level or top level in: (require xml) context...: /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/racket/private/reqprov.rkt:243:2 core225 render-to-file unpack62 loop (submod /home/kirrun/.racket/6.1.1/pkgs/pollen/command.rkt raco): [running body] /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/raco/raco.rkt: [running body] /nix/store/dzk1a00x1qv3pbzg3qway2bi7rzmfy5f-racket-6.1.1/share/racket/collects/raco/main.rkt: [running body] ``` In the future, I will want to use even more complex functions, potentially `require`d from other packages. So what is the recommended way to do this?
mbutterick commented 9 years ago (Migrated from github.com)

Not a dumb question, and I don’t think I cover it in the docs (though this is the sign I should).

As the error indicates, require has to be at the top level of the source file being evaluated. A template, however, is a code fragment that’s being pulled into that source file dynamically. (You’ve noticed that unlike other files in Pollen, a template doesn’t start with #lang pollen — because it’s not a standalone source file.) So a template can never use require, because it can’t reach the top level of the source file.

You have two options instead:

  1. local-require, which is basically the same as require (= imports identifiers into the namespace) but can be used in positions other than the top level.
(local-require xml)
(xexpr->string doc)
  1. dynamic-require, which is a form of require that can be used at runtime. Unlike require and local-require, the identifiers are passed as quoted-symbol arguments. Rather than importing identifiers, dynamic-require returns the value of the requested identifier. In this case, you get a function:
(define my-xexpr->string (dynamic-require 'xml 'xexpr->string))
(my-xexpr->string doc)

In HTML templates, you’ll probably want to use local-require.

Not a dumb question, and I don’t think I cover it in the docs (though this is the sign I should). As the error indicates, `require` has to be at the top level of the source file being evaluated. A template, however, is a code fragment that’s being pulled into that source file dynamically. (You’ve noticed that unlike other files in Pollen, a template doesn’t start with `#lang pollen` — because it’s not a standalone source file.) So a template can never use `require`, because it can’t reach the top level of the source file. You have two options instead: 1. [`local-require`](http://docs.racket-lang.org/reference/require.html?q=local-require#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._local-require%29%29), which is basically the same as `require` (= imports identifiers into the namespace) but can be used in positions other than the top level. ``` racket ◊(local-require xml) ◊(xexpr->string doc) ``` 1. [`dynamic-require`](http://docs.racket-lang.org/reference/Module_Names_and_Loading.html?q=dynamic-require#%28def._%28%28quote._~23~25kernel%29._dynamic-require%29%29), which is a form of `require` that can be used at runtime. Unlike `require` and `local-require`, the identifiers are passed as quoted-symbol arguments. Rather than importing identifiers, `dynamic-require` returns the value of the requested identifier. In this case, you get a function: ``` racket ◊(define my-xexpr->string (dynamic-require 'xml 'xexpr->string)) ◊(my-xexpr->string doc) ``` In HTML templates, you’ll probably want to use `local-require`.
kirelagin commented 9 years ago (Migrated from github.com)

Cool, thanks.

I wonder, what is that local context that the name I local-require in a template gets imported into. In other words, in what other unexpected places can this name potentially emerge?

Cool, thanks. I wonder, what is that local context that the name I `local-require` in a template gets imported into. In other words, in what other unexpected places can this name potentially emerge?
mbutterick commented 9 years ago (Migrated from github.com)

The scope of local-require is the lexical context where it’s used. (This almost means the expression where it’s used, but not quite, because some expressions (like begin) don’t create their own lexical context.)

In practice, the local-requires in a template don’t carry a risk of emerging elsewhere. Pollen renders a template by pulling it into a dynamically-generated lexical context that consists of a) the template code and b) the exports of the markup file (= metas, doc, here).

That said, I don’t find Racket’s approach to templates entirely satisfying. It would be more consistent to have template.html be a #lang pollen source file that can be independently tested and run.

The scope of `local-require` is the lexical context where it’s used. (This almost means the _expression_ where it’s used, but not quite, because some expressions (like `begin`) don’t create their own lexical context.) In practice, the `local-require`s in a template don’t carry a risk of emerging elsewhere. Pollen renders a template by [pulling it into](https://github.com/mbutterick/pollen/blob/master/render.rkt#L210) a dynamically-generated lexical context that consists of a) the template code and b) the exports of the markup file (= `metas`, `doc`, `here`). That said, I don’t find Racket’s approach to templates entirely satisfying. It would be more consistent to have `template.html` be a `#lang pollen` source file that can be independently tested and run.
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
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#65
Loading…
There is no content yet.