To “fn” or not to “fn”
That is the question.
Have you ever noticed that when people call built-in XQuery functions like string(), count(), and collection(), they sometimes prefix the function name with
fn:, and sometimes they don’t? Why? When is it necessary to include the
fn prefix, and when is it okay to leave it out? Or perhaps, you’ve noticed that sometimes MarkLogic complains when you leave it out, and sometimes it doesn’t? This blog post is an attempt to clear up any confusion around this, and to help you define your own policy or preference on this most important issue that affects us all.
fn is special in XQuery because it’s pre-defined to correspond to the namespace URI for built-in XPath functions. The upshot is that the following line is implicit in every XQuery module you write (unless you override it):
This means that you can call functions like
fn:concat(), because the
fn namespace is already defined for you. But if you leave off the prefix from a function call, then you are relying on the default function namespace, which is often the same as the
fn namespace, and so it often works. (Ay, there’s the rub.) Don’t worry, I’ll explain what I mean by “often.”
But first, it’s worth pointing out that things are a bit different in XSLT, where the prefix
fn does not have any special standing. If you want to write
fn:collection() in XSLT, you’d need to explicitly bind the
fn prefix in your stylesheet:
<xsl:stylesheet xmlns:fn="http://www.w3.org/2005/xpath-functions" ...>
Of course, nobody does that in practice, because
collection() works just fine, and in fact, always works fine without a prefix in XSLT. In other words, the XPath function namespace is always, not just often, the default function namespace in XSLT, and there’s no way to override it.
The difference in XQuery is that you can override the default function namespace, using a declaration like this:
declare default function namespace "http://example.com/my-function-ns";
If you don’t include a line like this in your code, then it’s up to your XQuery implementation to determine what the “default default” function namespace is. The XQuery spec “recommends” that implementations set that “default default” to the XPath function namespace (the “default default default”?), but it also allows them to override it. As it happens, MarkLogic is an implementation that overrides it…at least part of the time.
Okay, now I’ll explain what I mean by “often.” In XQuery, there are two kinds of modules: main modules and library modules. MarkLogic sets the default function namespace to the
fn namespace for main modules, but for library modules, the default function namespace is set to the module namespace. The nice thing about this is that, in library modules, you aren’t immediately forced to use a prefix on the functions that you define:
On the other hand, this means that you need to use the
fn: prefix on any calls you make to the built-in XPath functions. So a typical scenario is that
collection() works fine in your main module, but you have to write
fn:collection() in your library module. This may get annoying if you decide you want to re-use some of the code in your main module by moving it to a library module. In this case, cut-and-paste doesn’t cut it; you’ll have to add
fn: in a bunch of places. I think we can all agree that this is a sea of troubles worth taking arms against.
The question is: how you want to deal with it? I see two general approaches to avoiding the above pain:
- Always use the
- Explicitly declare the
fnnamespace as the default function namespace in library modules, so you never have to use the
Both methods are safe, so it largely comes down to personal preference. One thing to note about option 2 is that you’ll need to start using a prefix on your own module function definitions and function calls, something you have to do anyway when you’re calling them from elsewhere. If we really wanted to argue about it, I could give other reasons for one or the other (my personal preference is option 2), but I’ll leave that discussion to the comments — bring it on!
Before I leave you, I want to make sure you know how to accomplish option 2 and forever rid yourself of the need of remembering to type
fn:. Just put the following boilerplate in the prolog of each of your library modules: