How to overwrite built-in functions?

Hello,

I would like to change the behavior of the referencing command/alias “@”: I would like to be able to write @myreference[mycomment], and have the output be “(Author et al.)” instead of "(Author et al., mycomment), because I would like to make comments for myself in the source code, but not in the rendered document, and not have to write actual inline comments.

Or, more generally, how can I change the behavior of inbuilt functions, like “@”, “```” or “=”?

What I tried is to change the definition of the #cite and #ref commands like this:

#let myref(label, supplement:none, form:none) = {
    ref(label, supplement:supplement, form:"normal")
}
#let ref(label, comment, supplement:none, form:none) = {
    myref(label, supplement:supplement, form:"normal")
}

#let mycite(label, supplement:none, form:none) = {
    cite(label, supplement:supplement, form:"normal")
}
#let cite(label, comment, supplement:none, form:none) = {
    mycite(label, supplement:supplement, form:"normal")
}

and on their own they work like i described, but I would like to use this behavior with “@”

Thanks!

You can use a show rule that removes the supplement from the Reference Function – Typst Documentation. At the start of the show rule I check if the supplement is (already) none to avoid an infinite recursion. This will affect all references with @..., ref(...) and cite(...).

#show ref: it => {
  if it.supplement == none { return it }
  ref(it.target, supplement: none, form: it.form)
}

If you only want to target actual citation references, the show rule is

#show cite: it => {
  if it.supplement == none { return it }
  cite(it.key, supplement: none, form: it.form, style: it.style)
}

Personally, I would however just use actual comments if you never want them to show up in the rendered document anyway. The syntax highlighting alone makes it worth it to type the two additional characters (in my opinion).

This is @myreference[comment] with a show rule.
This is @myreference/*comment*/ with a comment.

To also bring some other aspects into this discussion:

If I understand correctly, the point here is to redefine ref while having access to the original. This can be simplified in a few ways; the most direct is this:

#let ref(label, supplement:none, form:none) = {
    std.ref(label, supplement:supplement, form:"normal")
}

built-in functions like ref are also available through the std module, exactly so that overriding is easier. In this case, you could even shorten this a bit further, like this:

#let ref(..args) = std.ref(..args, form: "normal")

the extra form: "normal" parameter will replace any form that was passed into this function.

However, the syntax shortcuts like @ref, _emph_ or = heading are shortcuts for the built-in functions, not for whatever function is currently available under some name! so while you can redefine the functions, you can’t redefine the syntax. For modifying what the syntaxes do, you’ll have to use show rules as demonstrated by @janekfleper.

1 Like

Hi, thanks for the responses! I agree that for this case inline comments are probably best. And thanks for showing the syntax for the overwriting the function, that is also really handy. I have some follow-up questions if that is appropriate here:

The show rules work when I put them into the same file, but when I try to add them in any place of my package/template they cause the error only element functions can be used as selectors. I think I understand what it means, but it is not clear to me why suddenly cite is not an element function anymore when applied in the template. The structure in the template looks like this:

#let template(doc) = [
    #show cite: it => {
        if it.supplement == none { return it }
        cite(it.key, supplement: none, form: it.form, style: it.style)
    }

    #doc
]

And I load it like this:

#import "@local/template:0.1.0": *
#show: template

I also tried playing around with the ..args spreading. Actually what I intended with my redefinitions was just to have the parameter comments be accepted by the new ref or cite definition, but be otherwise ignored. When I try that
I get

error: unexpected argument: comment
   ┌─ newtest.typ:16:35
   │
16 │ #ref(<referenceexample>, comment:"test")

so I tried to use the remove method on the comment argument before passing it to std.ref, before realizing that arguments are not arrays. But I still am a bit clueless how to pass trough only parts of the ..args without getting an unexpected argument error, I feel like that would be very useful to know.

All the best, sorry if the answers are obvious