`when/splice' does not work on template #132

Closed
opened 8 years ago by leafac · 8 comments
leafac commented 8 years ago (Migrated from github.com)

USE CASE

I want the <title> of the pages on my website to always contain the website name (e.g., ACME) and, if the particular page defines a meta called title (e.g., foo), include it too.

PRE-CONDITIONS

template.html.p

◊(define the-title (select-from-metas 'title metas))

<!DOCTYPE html>
◊(->html
 (html
  (head (title ◊when/splice[the-title]{◊the-title · } "ACME"))
  (apply body (select-from-doc 'root doc))))

index.html.pm

#lang pollen

Hi!

foo.html.pm

#lang pollen

◊define-meta[title]{Foo}

Foo.

CURRENT BEHAVIOR

Visiting localhost:8080/index.html shows the ACME title, as expected. Visiting localhost:8080/foo.html, on the other hand, fails with:

->html: contract violation
  expected: (or/c txexpr-element? txexpr-elements?)
  given: '(html (head (title ("Foo" " · ") "ACME")) (body "Foo."))
  in: the 1st argument of
      (->*
       ((or/c txexpr-element? txexpr-elements?))
       (#:attrs
        (or/c #f txexpr-attrs?)
        #:splice
        boolean?
        #:splice?
        boolean?
        #:tag
        (or/c #f txexpr-tag?))
       string?)
  contract from: 
      <pkgs>/pollen/pollen/template/html.rkt
  blaming: top-level
   (assuming the contract is correct)
  at: <pkgs>/pollen/pollen/template/html.rkt:12.26

Note that the title is ill-formed. It is (title ("Foo" " · ") "ACME") instead of the (title (@ "Foo" " · ") "ACME") I expected from using when/splice.

EXPECTED BEHAVIOR

when/splice should generate a list of the form (@ …), which would result in (title (@ "Foo" " · ") "ACME"), equivalent to (title "Foo" " · " "ACME") and the page would work.

WORKAROUNDS

BAD: On template.html.p, replace (title ◊when/splice[the-title]{◊the-title · } "ACME") with:

(apply title `(,@◊when/splice[the-title]{◊the-title · } "ACME"))

This fixes the case in which title is set (e.g., foo.html.pm), but breaks the case where it’s not (e.g., index.html.pm). The following error happens:

unquote-splicing: contract violation
  expected: list?
  given: ""

This is due to the inconsistent return type of when/splice (see https://github.com/mbutterick/pollen/pull/131).

BETTER: Add the following line to template.html.p:

◊(define (@ . elements) `(@ ,@elements))

Now both pages with and without the meta called title work.

I’m not sure why this works. Also, I thought that the default behavior for undefined functions would pick up @ as an undefined function and do what the above definition for @ does, but this doesn’t happen and I don’t know the reason either.


If there’s anything I can do to help, please let me know. I look forward to contributing back to Pollen, which is a life-changing piece of technology for me 😄

**USE CASE** I want the `<title>` of the pages on my website to always contain the website name (e.g., `ACME`) and, if the particular page defines a `meta` called `title` (e.g., `foo`), include it too. **PRE-CONDITIONS** `template.html.p` ``` ◊(define the-title (select-from-metas 'title metas)) <!DOCTYPE html> ◊(->html (html (head (title ◊when/splice[the-title]{◊the-title · } "ACME")) (apply body (select-from-doc 'root doc)))) ``` `index.html.pm` ``` #lang pollen Hi! ``` `foo.html.pm` ``` #lang pollen ◊define-meta[title]{Foo} Foo. ``` **CURRENT BEHAVIOR** Visiting `localhost:8080/index.html` shows the `ACME` title, as expected. Visiting `localhost:8080/foo.html`, on the other hand, fails with: ``` ->html: contract violation expected: (or/c txexpr-element? txexpr-elements?) given: '(html (head (title ("Foo" " · ") "ACME")) (body "Foo.")) in: the 1st argument of (->* ((or/c txexpr-element? txexpr-elements?)) (#:attrs (or/c #f txexpr-attrs?) #:splice boolean? #:splice? boolean? #:tag (or/c #f txexpr-tag?)) string?) contract from: <pkgs>/pollen/pollen/template/html.rkt blaming: top-level (assuming the contract is correct) at: <pkgs>/pollen/pollen/template/html.rkt:12.26 ``` Note that the `title` is ill-formed. It is `(title ("Foo" " · ") "ACME")` instead of the `(title (@ "Foo" " · ") "ACME")` I expected from using `when/splice`. **EXPECTED BEHAVIOR** `when/splice` should generate a list of the form `(@ …)`, which would result in `(title (@ "Foo" " · ") "ACME")`, equivalent to `(title "Foo" " · " "ACME")` and the page would work. **WORKAROUNDS** **BAD**: On `template.html.p`, replace `(title ◊when/splice[the-title]{◊the-title · } "ACME")` with: ``` (apply title `(,@◊when/splice[the-title]{◊the-title · } "ACME")) ``` This fixes the case in which `title` is set (e.g., `foo.html.pm`), but breaks the case where it’s not (e.g., `index.html.pm`). The following error happens: ``` unquote-splicing: contract violation expected: list? given: "" ``` This is due to the inconsistent return type of `when/splice` (see https://github.com/mbutterick/pollen/pull/131). **BETTER**: Add the following line to `template.html.p`: ``` ◊(define (@ . elements) `(@ ,@elements)) ``` Now both pages with and without the `meta` called `title` work. I’m not sure **why** this works. Also, I thought that the default behavior for undefined functions would pick up `@` as an undefined function and do what the above definition for `@` does, but this doesn’t happen and I don’t know the reason either. --- If there’s anything I can do to help, please let me know. I look forward to contributing back to Pollen, which is a life-changing piece of technology for me 😄
mbutterick commented 8 years ago (Migrated from github.com)

This problem stems from an earlier effort (commit 3f69df3ff5) to create one when/splice that applied to both X-expressions and text contexts, instead of a separate when/splice/text that would be required for text.

I think you've convinced me this effort has failed. This template illustrates the problem:

◊(define the-title (select-from-metas 'title metas))
<!DOCTYPE html>
◊(->html (body ◊when/splice[the-title]{◊the-title : } "ACME"))
◊when/splice[the-title]{◊the-title : } ACME
</html>

For this to work properly, the first when/splice needs to expand into an X-expression; the second when/splice needs to expand into a plain string. But there’s no way for when/splice to know about its context.

So, I agree it doesn't work quite right, but I'm also not yet convinced that continuing to patch when/splice is the right answer. Maybe it's wiser to replace it with something better.

This problem stems from an earlier effort (commit 3f69df3ff576fd61a3d3ecf502a2fabe6a3899da) to create one `when/splice` that applied to both X-expressions and text contexts, instead of a separate `when/splice/text` that would be required for text. I think you've convinced me this effort has failed. This template illustrates the problem: ``` ◊(define the-title (select-from-metas 'title metas)) <!DOCTYPE html> ◊(->html (body ◊when/splice[the-title]{◊the-title : } "ACME")) ◊when/splice[the-title]{◊the-title : } ACME </html> ``` For this to work properly, the first `when/splice` needs to expand into an X-expression; the second `when/splice` needs to expand into a plain string. But there’s no way for `when/splice` to know about its context. So, I agree it doesn't work quite right, but I'm also not yet convinced that continuing to patch `when/splice` is the right answer. Maybe it's wiser to replace it with something better.
mbutterick commented 8 years ago (Migrated from github.com)

PS In the meantime, you can work around the problem by using when/splice in a textual context rather than within an X-expression, e.g. —

◊(define the-title (select-from-metas 'title metas))
<!DOCTYPE html>
<head><title>◊when/splice[the-title]{◊the-title · }ACME</title></head>
◊(->html (apply body (select-from-doc 'root doc))))
</html>
PS In the meantime, you can work around the problem by using `when/splice` in a textual context rather than within an X-expression, e.g. — ``` ◊(define the-title (select-from-metas 'title metas)) <!DOCTYPE html> <head><title>◊when/splice[the-title]{◊the-title · }ACME</title></head> ◊(->html (apply body (select-from-doc 'root doc)))) </html> ```
mbutterick commented 8 years ago (Migrated from github.com)

PPS.

BETTER: Add the following line to template.html.p:

◊(define (@ . elements) `(@ ,@elements))
Now both pages with and without the meta called title work.

I’m not sure why this works.

The @ function is deliberately re-defined during a render to make it compatible with rendering into a textual context. So this fix works (within X-expressions) because you're restoring the original meaning of @.

PPS. > BETTER: Add the following line to template.html.p: > > ◊(define (@ . elements) `(@ ,@elements)) > Now both pages with and without the meta called title work. > > I’m not sure why this works. The `@` function is deliberately re-defined during a render to make it compatible with rendering into a textual context. So this fix works (within X-expressions) because you're restoring the original meaning of `@`.
leafac commented 8 years ago (Migrated from github.com)

I understand the situation better now, thank you for the careful and detailed explanation.

I’m in for the idea of having two flavors of when/splice. One for X-expression context (when/splice/xexpr) and one for text contexts (when/splice/text). Their return types would be consistent: when/splice/xexpr would return lists and when/splice/text would return strings (see https://github.com/mbutterick/pollen/pull/131 for how to get half-way there). That would require two flavors of splicing (@), too. Something like @/xexpr and @/text, maybe.

Of course, it would be up to you to decide which of when/splice/xexpr and when/splice/text is more relevant and should be promoted to when/splice.

I understand the situation better now, thank you for the careful and detailed explanation. I’m in for the idea of having two flavors of `when/splice`. One for X-expression context (`when/splice/xexpr`) and one for text contexts (`when/splice/text`). Their return types would be consistent: `when/splice/xexpr` would return lists and `when/splice/text` would return strings (see https://github.com/mbutterick/pollen/pull/131 for how to get half-way there). That would require two flavors of splicing (`@`), too. Something like `@/xexpr` and `@/text`, maybe. Of course, it would be up to you to decide which of `when/splice/xexpr` and `when/splice/text` is more relevant and should be promoted to `when/splice`.
mbutterick commented 8 years ago (Migrated from github.com)

I’ve pushed an update that I believe fixes when/splice (I overcame my earlier pessimism by trying harder). It changes when/splice to return a list (thereby also addressing #131) and amends Pollen’s lower-level text-output routine to cooperate with the splice tag (by ignoring it). I’ve tried your original example and it now works correctly. If you find other problems you can reopen this issue.

I’ve pushed an update that I believe fixes `when/splice` (I overcame my earlier pessimism by trying harder). It changes `when/splice` to return a list (thereby also addressing #131) and amends Pollen’s lower-level text-output routine to cooperate with the splice tag (by ignoring it). I’ve tried your original example and it now works correctly. If you find other problems you can reopen this issue.
mbutterick commented 8 years ago (Migrated from github.com)

by ignoring it

Clarification: the output routine will already splice a sublist of strings, so "foo" ("bar") "zam" becomes "foo" "bar" "zam". The change is if the sublist has a leading splice character, like "foo" (@ "bar") "zam", then the splice character will be ignored, so it will just become "foo" "bar" "zam" again.

> by ignoring it Clarification: the output routine will already splice a sublist of strings, so `"foo" ("bar") "zam"` becomes `"foo" "bar" "zam"`. The change is if the sublist has a leading splice character, like `"foo" (@ "bar") "zam"`, then the splice character will be ignored, so it will just become `"foo" "bar" "zam"` again.
leafac commented 8 years ago (Migrated from github.com)

I can confirm the fix is working for all my use cases. As always, thank you very much for Pollen ❤️

I can confirm the fix is working for all my use cases. As always, thank you very much for Pollen ❤️
mbutterick commented 8 years ago (Migrated from github.com)

🤘

:metal:
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#132
Loading…
There is no content yet.