You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I would like a way to deduplicate the specific string formatting {{build_dir}}/{{arg}}/{{file_name}} so that my uses of it don't become out of sync. My current solution is to use arg as a just variable instead of an argument, i.e. arg := "" and file_path := build_dir + "/" + arg + "/" + file_name at the top of the Justfile. This is imperfect because it requires passing the argument to the CLI as just pdf arg=foo instead of just pdf foo, and also it puts into global scope something that shouldn't necessarily be there.
But this is inelegant and doesn't allow the html and pdf targets to be run directly.
Is there a better way to accomplish this currently? If not, is it possible to add some syntax for declaring functions on strings? I'm imagining something like (this is obviously just a first draft at a syntax):
buildpath(s) := build_dir + "/" + s + "/" + file_name
pdf arg="":
pandoc input.md -o "{{buildpath(arg)}}.pdf"
I haven't looked super closely into the code base so I'm not sure how easy this would be. I'd be happy to look into writing a PR for this if it's desirable and feasible. Regardless, thanks for a great tool.
One thing that strikes me is that recipes are already similar to functions, so another option would be to allow calling recipes in expressions, which would return their standard output:
pubstructClosure<'src>{parameter:Name<'src>,// something a little more complex is actually needed to model multivariate functionsrule:Expression<'src>,}pubtypeNamedClosure<'src> = Binding<'src,Closure>
You could reuse Assignment here, but that requires Closure to be added to the Expression enum, which I think is not desirable, since you don't want a closure to be a subterm of an expression.
Parse closures, presumably by adding another case to the conditional here:
Evaluate closures to Functions via capturing the context. Maybe we only need a reference here or maybe we need to clone, I need to look more into it to know for sure - what do you think?
Add a variant of Thunk which doesn't need the function until evaluation time.
Obviously this is a pretty big change so I expect to run into other issues during implementation, but how do you feel about this general outline? I'm definitely open to other strategies.
(Of course error handling, tests, etc. are also necessary, this just describes the happy path.)
Obviously this is a pretty big change so I expect to run into other issues during implementation, but how do you feel about this general outline? I'm definitely open to other strategies.
The outline sounds great!
One general suggestion I'd make, since this is a big feature, is that anything that can reasonably be left for a follow-up PR should be. Better to get something bare-bones in than to make the PR harder to land. So tests and documentation should definitely be done as part of the PR, but if there are additional nice-to-have features that can be left out, they should be if they make the PR more complicated.
As for tests, I have a lot of unit tests, but I don't think it's important to have full unit test coverage. I like to have full test coverage via integration tests, and then I mostly use unit tests when something is tricky to write or debug, in which case I write unit tests expressing the correct behavior. So don't worry about unit tests unless you find them useful while developing.
If they're recursive, custom functions will probably push just into Turing-completeness, see #792.
Hi everybody, I'm really excited about working on this issue.
I'm not a pro with parser stuff, but i'm studying this code to figure out how hard it can be.
If there is someone who wants to pair with me, i would really appreciate it.
I would really love to want these custom functions feature 🚀
The [private] attribute may also be used to hide recipes or aliases without needing to change the name.
[...] This is useful for helper recipes which are only meant to be used as dependencies of other recipes.
I thought "dependency" meant that I could call another recipe and capture its output as described in this issue 😅
But unfortunately that's not possible (yet).
"Dependency" is meant in the sense of this phrase: "Dependencies run before recipes that depend on them".
This would be really nice to prevent logic duplication while keeping the code clean (no external shell invocations or similar workarounds). Is there already existing work on this?
I made them unstable, so you'll need set unstable, --unstable, or JUST_UNSTABLE=1 to use them.
The only reason I made them unstable is because I'm not totally sure about the syntax. The alternative that I'm considering is to use the fn keyword to introduce them:
fnfoo(a, b) := a + b
I personally like the syntax without fn better, but the syntax with fn would allow for easily grepping function definitions with ^fn, which is kind of nice.
I created issue #3248 to track stabilization. Please leave a comment about the syntax either way!
Reacted by Andre White, Paul Berg and Wasif Baig
Reacted by Paul Berg, Vincent Tencé, Ltrlg, Omar El Halabi, Maxim Borisov, Charles, Wasif Baig and Iago-lito
Reacted by Benoit de Chezelles, Dmitry Salin, Paul Berg, Splines and Charles
Activity
casey commentedon Jan 3, 2022
This is definitely desirable!
I like the syntax you proposed, i.e.:
One thing that strikes me is that recipes are already similar to functions, so another option would be to allow calling recipes in expressions, which would return their standard output:
This might be easier, since a lot of the infrastructure (error messages, etc) is already there.
rileyshahar commentedon Jan 4, 2022
Cool! Here are some initial thoughts on steps for implementation:
closure, to the grammar:You could reuse
Assignmenthere, but that requiresClosureto be added to theExpressionenum, which I think is not desirable, since you don't want a closure to be a subterm of an expression.Parse closures, presumably by adding another case to the conditional here:
just/src/parser.rs
Lines 340 to 345 in ef3629f
Evaluate closures to
Functions via capturing the context. Maybe we only need a reference here or maybe we need to clone, I need to look more into it to know for sure - what do you think?Add a variant of
Thunkwhich doesn't need the function until evaluation time.Obviously this is a pretty big change so I expect to run into other issues during implementation, but how do you feel about this general outline? I'm definitely open to other strategies.
(Of course error handling, tests, etc. are also necessary, this just describes the happy path.)
casey commentedon Jan 5, 2022
The outline sounds great!
One general suggestion I'd make, since this is a big feature, is that anything that can reasonably be left for a follow-up PR should be. Better to get something bare-bones in than to make the PR harder to land. So tests and documentation should definitely be done as part of the PR, but if there are additional nice-to-have features that can be left out, they should be if they make the PR more complicated.
As for tests, I have a lot of unit tests, but I don't think it's important to have full unit test coverage. I like to have full test coverage via integration tests, and then I mostly use unit tests when something is tricky to write or debug, in which case I write unit tests expressing the correct behavior. So don't worry about unit tests unless you find them useful while developing.
If they're recursive, custom functions will probably push
justinto Turing-completeness, see #792.yuri-potatoq commentedon Nov 5, 2023
Hi everybody, I'm really excited about working on this issue.
I'm not a pro with parser stuff, but i'm studying this code to figure out how hard it can be.
If there is someone who wants to pair with me, i would really appreciate it.
I would really love to want these custom functions feature 🚀
Splines commentedon Jul 3, 2024
I came here because I misunderstood this sentence:
I thought "dependency" meant that I could call another recipe and capture its output as described in this issue 😅
But unfortunately that's not possible (yet).
"Dependency" is meant in the sense of this phrase: "Dependencies run before recipes that depend on them".
[-]Allow custom functions for string manipulation[/-][+]User-defined functions[/+][-]User-defined functions[/-][+][feature] User-defined functions[/+]rougsig commentedon Aug 2, 2024
As a workaround:
[-][feature] User-defined functions[/-][+]User-defined functions[/+]CaptaiNiveau commentedon Mar 26, 2026
This would be really nice to prevent logic duplication while keeping the code clean (no external shell invocations or similar workarounds). Is there already existing work on this?
casey commentedon Apr 5, 2026
Added in #3247! They use the following syntax:
foo(a, b) := a + bI made them unstable, so you'll need
set unstable,--unstable, orJUST_UNSTABLE=1to use them.The only reason I made them unstable is because I'm not totally sure about the syntax. The alternative that I'm considering is to use the
fnkeyword to introduce them:I personally like the syntax without
fnbetter, but the syntax withfnwould allow for easily grepping function definitions with^fn, which is kind of nice.I created issue #3248 to track stabilization. Please leave a comment about the syntax either way!
CaptaiNiveau commentedon Apr 7, 2026
Awesome! I'll check them out tomorrow :D