improve docs for metas (closes #22)

pull/27/head
Matthew Butterick 10 years ago
parent be3260cd7e
commit 5d43602d08

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,7 +1,7 @@
#lang scribble/manual #lang scribble/manual
@(require scribble/bnf scribble/eval "utils.rkt" @(require scribble/bnf scribble/eval "utils.rkt" "mb-tools.rkt"
(for-syntax racket/base) (for-syntax racket/base)
(for-label pollen/world (only-in scribble/reader (for-label pollen/world pollen/render pollen/template (only-in scribble/reader
use-at-readtable))) use-at-readtable)))
@(define read-eval (make-base-eval)) @(define read-eval (make-base-eval))
@ -24,9 +24,9 @@ I chose the lozenge as the command marker because a) it appears in almost every
Here's how you type it: Here's how you type it:
@bold{Mac}: option + shift + V @bold{Mac}: option + shift + V
@(linebreak)
@bold{Windows}: holding down alt, type 9674 on the num pad @bold{Windows}: holding down alt, type 9674 on the num pad
@(linebreak)
@bold{Ubuntu}: ctrl + shift + U, then 25CA @bold{Ubuntu}: ctrl + shift + U, then 25CA
Still, if you don't want to use the lozenge as your command marker, you can use something else. Set Pollen's @racket[world:command-marker] value to whatever character you want. Still, if you don't want to use the lozenge as your command marker, you can use something else. Set Pollen's @racket[world:command-marker] value to whatever character you want.
@ -65,23 +65,23 @@ Each of the three parts is optional. You can also nest commands within each othe
Here are a few examples of correct text-mode commands: Here are a few examples of correct text-mode commands:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊variable-name ◊variable-name
◊tag{Text inside the tag.} ◊tag{Text inside the tag.}
◊tag['attr: "value"]{Text inside the tag} ◊tag['attr: "value"]{Text inside the tag}
◊get-customer-id["Brennan Huff"] ◊get-customer-id["Brennan Huff"]
◊tag{His ID is ◊get-customer-id["Brennan Huff"].} ◊tag{His ID is ◊get-customer-id["Brennan Huff"].}
}| }
And some incorrect examples: And some incorrect examples:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊tag {Text inside the tag.} ; space between first and second parts ◊tag {Text inside the tag.} ; space between first and second parts
◊tag[Text inside the tag] ; text argument needs to be within braces ◊tag[Text inside the tag] ; text argument needs to be within braces
◊tag{Text inside the tag}['attr: "value"] ; wrong order ◊tag{Text inside the tag}['attr: "value"] ; wrong order
}| }
The next section describes each of these parts in detail. The next section describes each of these parts in detail.
@ -89,23 +89,23 @@ The next section describes each of these parts in detail.
If you're familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It's simple: any Racket expression can become a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code: If you're familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It's simple: any Racket expression can become a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code:
@codeblock|{ @codeblock{
#lang racket #lang racket
(define song "Revolution") (define song "Revolution")
(format "~a #~a" song (* 3 3)) (format "~a #~a" song (* 3 3))
}| }
Can be converted to Pollen like so: Can be converted to Pollen like so:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊(define song "Revolution") ◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3)) ◊(format "~a #~a" song (* 3 3))
}| }
And in DrRacket, they produce the same output: And in DrRacket, they produce the same output:
@nested[#:style 'inset]{@racketoutput{Revolution #9}} @repl-output{Revolution #9}
Beyond that, there's not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command. Beyond that, there's not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command.
@ -114,33 +114,36 @@ Beyond that, there's not much to say about Racket mode — any valid expression
Even if you don't plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in text mode to Racket mode. So a text-mode command that looks like this: Even if you don't plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in text mode to Racket mode. So a text-mode command that looks like this:
@racketblock[ @codeblock[#:keep-lang-line? #f]{
#lang pollen
◊headline[#:size 'enormous]{Man Bites Dog!} ◊headline[#:size 'enormous]{Man Bites Dog!}
] }
Is actually being turned into a Racket-mode command like this: Is actually being turned into a Racket-mode command like this:
@racketblock[ @codeblock[#:keep-lang-line? #f]{
#lang racket
(headline #:size 'enormous "Man Bites Dog!") (headline #:size 'enormous "Man Bites Dog!")
] }
Thus a text-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.) Thus a text-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.)
The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode: The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊(define song "Revolution") ◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3)) ◊(format "~a #~a" song (* 3 3))
}| }
Can be rewritten using text mode: Can be rewritten using text mode:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊define[song]{Revolution} ◊define[song]{Revolution}
◊format["~a #~a" song (* 3 3)] ◊format["~a #~a" song (* 3 3)]
}| }
And it will work the same way. And it will work the same way.
@ -154,44 +157,47 @@ In Pollen, you'll typically use the command name for one of four purposes:
@item{To invoke a tag function.} @item{To invoke a tag function.}
@item{To invoke another function.} @item{To invoke another function.}
@item{To insert the value of a variable.} @item{To insert the value of a variable.}
@item{To insert a @tt{meta} value.}
@item{To insert a comment.} @item{To insert a comment.}
] ]
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@subsubsection{Invoking tag functions} @subsubsection{Invoking tag functions}
By default, Pollen treats every command name as a @italic{tag function}. As the name implies, a tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content. By default, Pollen treats every command name as a @italic{tag function}. The default tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊strong{Fancy Sauce, $1} ◊strong{Fancy Sauce, $1}
}| }
@racketoutput{@literal{'(strong "Fancy Sauce, $1")}} @repl-output{'(strong "Fancy Sauce, $1")}
To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor does it make you define your tag functions ahead of time. Just type a tag, and you can start using it. To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor does it make you define your tags ahead of time. Just type a tag, and you can start using it.
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊utterlyridiculoustagname{Oh really?} ◊utterlyridiculoustagname{Oh really?}
}| }
@racketoutput{@literal{'(utterlyridiculoustagname "Oh really?")}}
@repl-output{'(utterlyridiculoustagname "Oh really?")}
The one restriction is that you can't invent names for tag functions that are already being used for other commands. For instance, @tt{map} is a name permanently reserved by the Racket function @racket[map]. It's also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other @racket[map]:
The one restriction is that you can't invent names for tags that are already being used for other commands. For instance, @tt{map} is a name permanently reserved by the Racket function @racket[map]. It's also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other @racket[map]:
@codeblock|{
@codeblock{
#lang pollen #lang pollen
◊map{Fancy Sauce, $1} ◊map{Fancy Sauce, $1}
}| }
@racketerror{map: arity mismatch;@(linebreak) @errorblock{
the expected number of arguments does not match the given number@(linebreak) map: arity mismatch;
  given: 1@(linebreak) the expected number of arguments does not match the given number
  arguments...:@(linebreak)   given: 1
  arguments...:
    "Fancy Sauce, $1"}     "Fancy Sauce, $1"}
What to do? Read on. What to do? Read on.
@ -199,42 +205,42 @@ What to do? Read on.
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@subsubsection{Invoking other functions} @subsubsection{Invoking other functions}
Though every command name starts out as a tag function, it doesn't necessarily end there. You have two options for invoking other functions: defining your own , or invoking others from Racket. Though every command name starts out as a default tag function, it doesn't necessarily end there. You have two options for invoking other functions: defining your own, or invoking others from Racket.
@bold{Defining your own functions} @bold{Defining your own functions}
Use the @racket[define] command to create your own function for a command name. After that, when you use the command name, you'll get the new behavior. For instance, recall this example showing the default tag-function behavior: Use the @racket[define] command to create your own function for a command name. After that, when you use the command name, you'll get the new behavior. For instance, recall this example showing the default tag-function behavior:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊strong{Fancy Sauce, $1} ◊strong{Fancy Sauce, $1}
}| }
@racketoutput{@literal{'(strong "Fancy Sauce, $1")}} @repl-output{'(strong "Fancy Sauce, $1")}
We can define @tt{strong} to do something else, like add to the text: We can define @tt{strong} to do something else, like add to the text:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text))) ◊(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text)))
◊strong{Fancy Sauce, $1} ◊strong{Fancy Sauce, $1}
}| }
@racketoutput{@literal{'(strong "Hey! Listen up! Fancy Sauce, $1")}} @repl-output{'(strong "Hey! Listen up! Fancy Sauce, $1")}
The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @tt{strong} is going to get a text argument that it's not defined to handle: The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @tt{strong} is going to get a text argument that it's not defined to handle:
@codeblock|{ @codeblock{
#lang pollen #lang pollen
◊(define (strong) '(fib "1 1 2 3 5 8 13 ...")) ◊(define (strong) '(fib "1 1 2 3 5 8 13 ..."))
◊strong{Fancy Sauce, $1} ◊strong{Fancy Sauce, $1}
}| }
@racketerror{strong: arity mismatch;@(linebreak) @errorblock{strong: arity mismatch;
the expected number of arguments does not match the given number@(linebreak) the expected number of arguments does not match the given number
  expected: 0@(linebreak)   expected: 0
  given: 1@(linebreak)   given: 1
  arguments...:@(linebreak)   arguments...:
    "Fancy Sauce, $1"}     "Fancy Sauce, $1"}
Whereas in this version, @tt{strong} accepts an argument called @tt{text}, but then ignores it: Whereas in this version, @tt{strong} accepts an argument called @tt{text}, but then ignores it:
@ -245,7 +251,7 @@ Whereas in this version, @tt{strong} accepts an argument called @tt{text}, but t
◊strong{Fancy Sauce, $1} ◊strong{Fancy Sauce, $1}
}| }|
@racketoutput{@literal{'(fib "1 1 2 3 5 8 13 ...")}} @repl-output{'(fib "1 1 2 3 5 8 13 ...")}
You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing. You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing.
@ -259,9 +265,9 @@ You aren't limited to functions you define. Any function from Racket, or any Rac
◊range[1 20] ◊range[1 20]
}| }|
@racketoutput{@literal{'(range 1 20)}} @repl-output{'(range 1 20)}
Hold on — that's not what we want. Where's the list of numbers? The problem here is that we didn't explicitly import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @tt{range} as a tag function (and if we had been, then @racketoutput{@literal{'(range 1 20)}} would've been the right result). Hold on — that's not what we want. Where's the list of numbers? The problem here is that we didn't explicitly import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @tt{range} as a tag function (and if we had been, then @repl-output{'(range 1 20)} would've been the right result).
We fix this by using the @racket[require] command to bring in the @racketmodname[racket/list] library, which contains the @racket[range] we want: We fix this by using the @racket[require] command to bring in the @racketmodname[racket/list] library, which contains the @racket[range] we want:
@ -271,7 +277,7 @@ We fix this by using the @racket[require] command to bring in the @racketmodname
◊range[1 20] ◊range[1 20]
}| }|
@racketoutput{@literal{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}} @repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names: Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names:
@ -282,7 +288,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to
◊rick[1 20] ◊rick[1 20]
}| }|
@racketoutput{@literal{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}} @repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
Let's return to the problem that surfaced in the last section — the fact that some command names can't be used as tag functions because they're already being used for other things. You can work around this by defining your own tag function with a non-conflicting name. Let's return to the problem that surfaced in the last section — the fact that some command names can't be used as tag functions because they're already being used for other things. You can work around this by defining your own tag function with a non-conflicting name.
@ -294,7 +300,7 @@ For instance, suppose we want to use @tt{map} as a tag even though Racket is usi
◊my-map{How I would love this to be a map.} ◊my-map{How I would love this to be a map.}
}| }|
@racketoutput{@literal{'(my-map "How I would love this to be a map.")}} @repl-output{'(my-map "How I would love this to be a map.")}
But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @tt{map}. We can do this with the Pollen helper @racket[make-default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too: But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @tt{map}. We can do this with the Pollen helper @racket[make-default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too:
@ -307,10 +313,12 @@ But @code{my-map} is not the tag we want. We need to define @code{my-map} to be
◊my-map{How I would love this to be a map.} ◊my-map{How I would love this to be a map.}
}| }|
@racketoutput{@literal{'(map "How I would love this to be a map.")}} @repl-output{'(map "How I would love this to be a map.")}
Problem solved. Problem solved.
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@subsubsection{Inserting the value of a variable} @subsubsection{Inserting the value of a variable}
@ -322,7 +330,7 @@ A Pollen command name usually refers to a function, but it can also refer to a @
The value of foo is ◊foo The value of foo is ◊foo
}| }|
@racketoutput{@literal{The value of foo is bar}} @repl-output{The value of foo is bar}
Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won't work, because a variable is not a function: Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won't work, because a variable is not a function:
@ -337,13 +345,15 @@ The value of foo is ◊foo[]
}| }|
@racketerror{application: not a procedure;@(linebreak) @errorblock{application: not a procedure;
expected a procedure that can be applied to arguments@(linebreak) expected a procedure that can be applied to arguments
  given: "bar"@(linebreak)   given: "bar"
  arguments...: [none]}   arguments...: [none]}
The reason we can simply drop @code{◊foo} into the text argument of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value). When appropriate, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted: The reason we can simply drop @code{◊foo} into the text argument of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value).
In preprocessor source files, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:
@codeblock|{ @codeblock|{
#lang pollen #lang pollen
@ -351,7 +361,10 @@ The reason we can simply drop @code{◊foo} into the text argument of another Po
The value of zam is ◊zam The value of zam is ◊zam
}| }|
@racketoutput{@literal{The value of zam is 42}} @repl-output{The value of zam is 42}
@margin-note{In an unsaved DrRacket file, or a file without a special Pollen source extension, the @tt{#lang pollen} designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor mode by starting a file with @tt{#lang pollen/pre}. See also @secref["Preprocessor___pp_extension_"
#:doc '(lib "pollen/scribblings/pollen.scrbl")].}
If the variable holds a container datatype (like a @racket[list], @racket[hash], or @racket[vector]), Pollen will produce the Racket text representation of the item. Here, @tt{zam} is a @racket[list] of integers: If the variable holds a container datatype (like a @racket[list], @racket[hash], or @racket[vector]), Pollen will produce the Racket text representation of the item. Here, @tt{zam} is a @racket[list] of integers:
@ -361,9 +374,9 @@ If the variable holds a container datatype (like a @racket[list], @racket[hash],
The value of zam is ◊zam The value of zam is ◊zam
}| }|
@racketoutput{@literal{The value of zam is '(1 2 3)}} @repl-output{The value of zam is '(1 2 3)}
This feature is included for your convenience as an author. But in general, your readers won't want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using @racket[string-join] from the @racketmodname[racket/string] library: This feature is included for your convenience. But in general, your readers won't want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using @racket[string-join] from the @racketmodname[racket/string] library:
@codeblock|{ @codeblock|{
#lang pollen #lang pollen
@ -372,7 +385,7 @@ This feature is included for your convenience as an author. But in general, your
The value of zam is ◊string-join[(map number->string zam)]{ and } The value of zam is ◊string-join[(map number->string zam)]{ and }
}| }|
@racketoutput{@literal{The value of zam is 1 and 2 and 3}} @repl-output{The value of zam is 1 and 2 and 3}
Pollen will still produce an error if you try to convert an esoteric value to a string. Here, @tt{zam} is the addition function (@racket[+]): Pollen will still produce an error if you try to convert an esoteric value to a string. Here, @tt{zam} is the addition function (@racket[+]):
@ -382,8 +395,21 @@ Pollen will still produce an error if you try to convert an esoteric value to a
The value of zam is ◊zam The value of zam is ◊zam
}| }|
@racketerror{Pollen decoder: can't convert #<procedure:+> to string} @errorblock{Pollen decoder: can't convert #<procedure:+> to string}
Moreover, Pollen will not perform @italic{any} automatic text conversion in Pollen markup source files. Suppose we take the example above — which worked as a preprocessor source file — and change the language to @racket[pollen/markup]:
@codeblock|{
#lang pollen/markup
◊(define zam (list 1 2 3))
The value of zam is ◊zam
}|
This time, the file will produce an error:
@errorblock{
pollen markup error: in '(root "The value of zam is " (1 2 3)), '(1 2 3) is not a valid element (must be txexpr, string, symbol, XML char, or cdata)
}
One special case to know about. In the examples above, there's a word space between the variable and the other text. But suppose you need to insert a variable into text so that there's no space in between. The simple ◊ notation won't work, because it won't be clear where the variable name ends and the text begins. One special case to know about. In the examples above, there's a word space between the variable and the other text. But suppose you need to insert a variable into text so that there's no space in between. The simple ◊ notation won't work, because it won't be clear where the variable name ends and the text begins.
@ -395,7 +421,7 @@ For instance, suppose we want to use a variable @tt{edge} next to the string @t
p { margin-left: ◊edgepx; } p { margin-left: ◊edgepx; }
}| }|
@racketerror{Pollen decoder: can't convert #<procedure:...t/pollen/tag.rkt:6:2> to string} @errorblock{Pollen decoder: can't convert #<procedure:...t/pollen/tag.rkt:6:2> to string}
The example fails because Pollen reads the whole string after the @litchar{◊} as the single variable name @tt{edgepx}. Since @tt{edgepx} isn't defined, it's treated as a tag function, and since Pollen can't convert a function to a string, we get an error. The example fails because Pollen reads the whole string after the @litchar{◊} as the single variable name @tt{edgepx}. Since @tt{edgepx} isn't defined, it's treated as a tag function, and since Pollen can't convert a function to a string, we get an error.
@ -407,7 +433,7 @@ In these situations, surround the variable name with vertical bars @litchar{◊|
p { margin-left: ◊|edge|px; } p { margin-left: ◊|edge|px; }
}| }|
@racketoutput{p { margin-left: 100px; }} @repl-output{p { margin-left: 100px; }}
If you use this notation when you don't need to, nothing bad will happen. The vertical bars are always ignored. If you use this notation when you don't need to, nothing bad will happen. The vertical bars are always ignored.
@ -417,7 +443,82 @@ If you use this notation when you don't need to, nothing bad will happen. The ve
The value of edge is ◊|edge| pixels} The value of edge is ◊|edge| pixels}
}| }|
@racketoutput{The value of edge is 100 pixels} @repl-output{The value of edge is 100 pixels}
@;--------------------------------------------------------------------
@subsubsection{Inserting metas}
@italic{Metas} are keyvalue pairs embedded in a source file that are not included in the main output when the source is run, and collected into a separate hash table.
Metas are not a foundational abstraction. They're just a convenience — a place to store arbitrary pieces of information that you might want to use later.
@margin-note{Pollen occasionally relies on metas. For instance, the @racket[get-template-for] function will look in the metas of a source file to see if a template is explicitly specified. The @racket[pollen/template] module also contains functions for working with metas, such as @racket[select-from-metas].}
To insert a meta, use the standard command syntax for inserting a tag with an attribute pair, but use the special @tt{meta} name:
@codeblock{
#lang pollen
◊some-tag['key: "value"]{Normal tag}
◊meta['dog: "Roxy"]
◊some-tag['key: "value"]{Another normal tag}
}
When you mark a meta like this, two things happen. First, when you run the file, the meta is removed from the result:
@repl-output{
'(some-tag ((key "value")) "Normal tag")
'(some-tag ((key "value")) "Another normal tag")
}
Second, the meta is collected into a hash table that is exported with the name @tt{metas}. To see this hash table, run the file above in DrRacket, then move to the interactions window and type @exec{metas} at the prompt:
@terminal{
> metas
'#hash((here-path . "unsaved-editor167056") (dog . "Roxy"))
}
The only key that's automatically defined in every meta table is @tt{here-path}, which is the absolute path to the source file. (Here, because the file hasn't been saved, you'll see the @tt{unsaved-editor...} name instead.)
Still, you can override this too:
@codeblock{
#lang pollen
◊some-tag['key: "value"]{Normal tag}
◊meta['dog: "Roxy"]
◊some-tag['key: "value"]{Another normal tag}
◊meta['here-path: "nowhere"]
}
When you run this code, the result will be the same as before, but this time the metas will be different:
@terminal{
> metas
'#hash((dog . "Roxy") (here-path . "nowhere"))
}
It doesn't matter how many metas you put in a source file or where you put them. They'll all be extracted and put into the @tt{metas} hash table. The order of the metas is not preserved (because order is not preserved in a hash table). But if you have two metas with the same key, the later one will supersede the earlier one:
@codeblock{
#lang pollen
◊some-tag['key: "value"]{Normal tag}
◊meta['dog: "Roxy"]
◊some-tag['key: "value"]{Another normal tag}
◊meta['dog: "Lex"]
}
Though there are two metas named @racket['dog], only the second one persists:
@terminal{
> metas
'#hash((dog . "Lex") (here-path . "unsaved-editor167056"))
}
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@ -433,8 +534,8 @@ To comment out the rest of a single line, use a lozenge followed by a semicolon
◊span{Nor is this} ◊;span{But this is} ◊span{Nor is this} ◊;span{But this is}
}| }|
@racketoutput{@literal{'(span "This is not a comment")} @(linebreak) @repl-output{'(span "This is not a comment")
@literal{'(span "Nor is this")}} '(span "Nor is this")}
To comment out a multiline block, use the lozengesemicolon signal @litchar{◊;} with curly braces, @litchar{◊;@"{"}like so@litchar{@"}"}. To comment out a multiline block, use the lozengesemicolon signal @litchar{◊;} with curly braces, @litchar{◊;@"{"}like so@litchar{@"}"}.
@ -448,7 +549,7 @@ Actually, it's all a comment now
}| }|
@racketoutput{@literal{Actually, it's all a comment now}} @repl-output{Actually, it's all a comment now}
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@subsection{The Racket arguments} @subsection{The Racket arguments}
@ -462,11 +563,11 @@ For instance, tag functions. Recall from before that any not-yet-defined command
◊title{The Beginning of the End} ◊title{The Beginning of the End}
}| }|
@racketoutput{@literal{'(title "The Beginning of the End")}} @repl-output{'(title "The Beginning of the End")}
But what if you wanted to add attributes to this tag, so that it comes out like this? But what if you wanted to add attributes to this tag, so that it comes out like this?
@racketoutput{@literal{'(title ((class "red")(id "first")) "The Beginning of the End")}} @repl-output{'(title ((class "red")(id "first")) "The Beginning of the End")}
You can do it with Racket arguments. You can do it with Racket arguments.
@ -477,7 +578,7 @@ Here's the hard way. You can type out your list of attributes in Racket format a
◊title['((class "red")(id "first"))]{The Beginning of the End} ◊title['((class "red")(id "first"))]{The Beginning of the End}
}| }|
@racketoutput{@literal{'(title ((class "red") (id "first")) "The Beginning of the End")}} @repl-output{'(title ((class "red") (id "first")) "The Beginning of the End")}
But that's a lot of parentheses to think about. So here's the easy way. Anytime you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @racket[symbol] / @racket[string] pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark @litchar{'} and end with a colon @litchar{:}. So taken together, they look like this: But that's a lot of parentheses to think about. So here's the easy way. Anytime you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @racket[symbol] / @racket[string] pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark @litchar{'} and end with a colon @litchar{:}. So taken together, they look like this:
@ -487,7 +588,7 @@ But that's a lot of parentheses to think about. So here's the easy way. Anytime
◊title['class: "red" 'id: "first"]{The Beginning of the End} ◊title['class: "red" 'id: "first"]{The Beginning of the End}
}| }|
@racketoutput{@literal{'(title ((class "red") (id "first")) "The Beginning of the End")}} @repl-output{'(title ((class "red") (id "first")) "The Beginning of the End")}
Racket arguments can be any valid Racket expressions. For instance, this will also work: Racket arguments can be any valid Racket expressions. For instance, this will also work:
@ -496,7 +597,7 @@ Racket arguments can be any valid Racket expressions. For instance, this will al
◊title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End} ◊title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End}
}| }|
@racketoutput{@literal{'(title ((class "42") (id "first")) "The Beginning of the End")}} @repl-output{'(title ((class "42") (id "first")) "The Beginning of the End")}
Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we'll define a variable called @tt{name} and use it in the Racket arguments of @tt{title}: Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we'll define a variable called @tt{name} and use it in the Racket arguments of @tt{title}:
@ -506,7 +607,7 @@ Since Pollen commands are really just Racket arguments underneath, you can use t
◊title['class: "red" 'id: ◊name]{The Beginning of the End} ◊title['class: "red" 'id: ◊name]{The Beginning of the End}
}| }|
@racketoutput{@literal{'(title ((class "read") (id "Brennan")) "The Beginning of the End")}} @repl-output{'(title ((class "read") (id "Brennan")) "The Beginning of the End")}
You can also use this area for @italic{keyword arguments}. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the @tt{h1 ... h6} tags, you want to consolidate them into one command called @tt{heading} and select the level separately. You can do this with a keyword, in this case @racket[#:level], which is passed as a Racket argument: You can also use this area for @italic{keyword arguments}. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the @tt{h1 ... h6} tags, you want to consolidate them into one command called @tt{heading} and select the level separately. You can do this with a keyword, in this case @racket[#:level], which is passed as a Racket argument:
@ -520,9 +621,9 @@ You can also use this area for @italic{keyword arguments}. Keyword arguments can
◊heading[#:level 6]{Trivial league} ◊heading[#:level 6]{Trivial league}
}| }|
@racketoutput{@literal{'(h1 "Major league")} @(linebreak) @repl-output{'(h1 "Major league")
@literal{'(h2 "Minor league")} @(linebreak) '(h2 "Minor league")
@literal{'(h6 "Trivial league")} '(h6 "Trivial league")
} }
@;-------------------------------------------------------------------- @;--------------------------------------------------------------------
@ -535,7 +636,7 @@ The third part of a text-mode Pollen command is the text argument. The text argu
◊div{Do it again. ◊div{And again. ◊div{And yet again.}}} ◊div{Do it again. ◊div{And again. ◊div{And yet again.}}}
}| }|
@racketoutput{@literal{'(div "Do it again. " (div "And again. " (div "And yet again.")))}} @repl-output{'(div "Do it again. " (div "And again. " (div "And yet again.")))}
Three small details to know about the text argument. Three small details to know about the text argument.
@ -546,7 +647,7 @@ First, the only character that needs special handling in a text argument is the
◊definition{This is the lozenge: ◊"◊"} ◊definition{This is the lozenge: ◊"◊"}
}| }|
@racketoutput{@literal{'(definition "This is the lozenge: ◊")}} @repl-output{'(definition "This is the lozenge: ◊")}
Second, the whitespace-trimming policy. Here's the short version: if there's a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends: Second, the whitespace-trimming policy. Here's the short version: if there's a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends:
@ -559,7 +660,7 @@ I agree.
} }
}| }|
@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} @repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
Yields the same result as this one: Yields the same result as this one:
@ -570,18 +671,18 @@ Yields the same result as this one:
I agree.} I agree.}
}| }|
@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} @repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
For the long version, please see [future link: Spaces, Newlines, and Indentation]. For the long version, please see [future link: Spaces, Newlines, and Indentation].
Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this: Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this:
@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} @repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
Instead of this: Instead of this:
@racketoutput{@literal{'(div "Roomy!\n\nI agree.")}} @repl-output{'(div "Roomy!\n\nI agree.")}
Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this @tt{jejune} function only accepts a single argument. It will work with a single-line text argument, because that produces a single string: Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this @tt{jejune} function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:
@ -592,7 +693,7 @@ Under most circumstances, these two tagged X-expressions will behave the same wa
◊jejune{Irrational confidence} ◊jejune{Irrational confidence}
}| }|
@racketoutput{@literal{'(jejune "Irrational confidence")}} @repl-output{'(jejune "Irrational confidence")}
But watch what happens with a multiline text argument: But watch what happens with a multiline text argument:
@ -604,13 +705,13 @@ But watch what happens with a multiline text argument:
chastened} chastened}
}| }|
@racketerror{jejune: arity mismatch;@(linebreak) @errorblock{jejune: arity mismatch;
the expected number of arguments does not match the given number@(linebreak) the expected number of arguments does not match the given number
  expected: 1@(linebreak)   expected: 1
  given: 3@(linebreak)   given: 3
  arguments...:@(linebreak)   arguments...:
   "Deeply"@(linebreak)    "Deeply"
   "\n"@(linebreak)    "\n"
   "chastened"}    "chastened"}
The answer is to use a @italic{rest argument} in the function, which takes the ``rest'' of the arguments — however many there may be — and combines them into a single @racket[list]. If we rewrite @tt{jejune} with a rest argument, we can fix the problem: The answer is to use a @italic{rest argument} in the function, which takes the ``rest'' of the arguments — however many there may be — and combines them into a single @racket[list]. If we rewrite @tt{jejune} with a rest argument, we can fix the problem:
@ -623,7 +724,8 @@ The answer is to use a @italic{rest argument} in the function, which takes the `
chastened} chastened}
}| }|
@racketoutput{@literal{'(jejune "Deeply" "\n" "chastened")}} @repl-output{'(jejune "Deeply" "\n" "chastened")}
@section{Further reading} @section{Further reading}

Loading…
Cancel
Save