If you can pass values for the arguments via acceptable I/O methods, it's acceptable.
On PPCG, we allow a "program" to be a "full program" or a "function". Let's consider these two cases separately:
A full program on PPCG is fairly objectively definable; we take the language implementation (in this case, the Brachylog interpreter), give it the program as an argument (typically via a file), and run it to see what happens. The program can take and produce output via any of the acceptable I/O methods. Nothing here is specific to Brachylog.
Out of the legal means of I/O for full programs, the following make sense in the context of Brachylog:
- We can take input via standard input. If input is provided on standard input, the Brachylog interpreter interprets that as setting the Input argument to the program. So far, so good; I doubt there's any controversy that setting the Input to the program is allowed, and this is the way we do it. (Note: In a full program, giving it Input is only acceptable because the interpreter will set the Input as a consequence of text on standard input; having something named Input in a language wouldn't be sufficient in a full program to allow the user to provide input, unless the interpreter gave some mechanism of doing so.)
- We can take input via a command line argument. If input is provided via the first command line argument, the Brachylog interpreter interprets that as setting the Output argument to the program. The fact that it's named Output has nothing to do with the actual mechanism, though; what matters is that we're providing input to the program in a legal way, and what the interpreter does with it is its problem.
- We can combine the above two rules to provide two parameters to the program, the Input on standard input and the Output via an argument. (As it happens, I personally disagree with this rule, but it's our current consensus and I'm quite comprehensively outvoted on it. So this post is describing what the rules currently are.)
- We can take output via standard output. By default, the Brachylog interpreter writes
false. here. If the program contains a
w command (or one of its variants), it instead allows the
w command to provide standard output.
Note what we can't do: we can't take output via the Output, or via the Input. The mechanism via which you do this in Brachylog is to provide a variable name in one of these places (
Z is common), and then the interpreter will output text like
Z = 4. This means that a Brachylog full program can only provide output via two mechanisms: the
w command family; and implicit printing of
false.. This means that in a decision-problem, a full program is likely going to be the best idea. In other cases, using a full program is inadvisable because it can require quite a bit of boilerplate.
Here's an example of a full program that takes input via its Output, and produces output on standard output using the
w command. As can be seen, it's much more verbose than just writing it "normally" would be, as a consequence of Brachylog's evaluation order (
w takes input only from the left, whereas an Output argument provides input from the right, so you need contortions to get the program back into sequence). As such, although this example is legal, it probably isn't very useful for golfing.
A function on PPCG is less objectively definable. However, a Brachylog predicate is a clear equivalent of a function in most languages; it has arguments, it's reusable, it can be called multiple times.
Out of the legal means of I/O for functions, the following make sense in the context of Brachylog:
- We can take input via function arguments. All Brachylog functions have exactly two arguments (named
., or Input and Output). The two are treated asymmetrically (because the Input argument naturally occurs early in the evaluation order in Brachylog's syntax, and the Output argument naturally occurs late), but this doesn't change the fact that they're both arguments, and they're both capable of providing input to the function in the normal way. If I have a function
↰₀, I can provide it the integers 1 and 2 as arguments by writing
1↰₀2. So both arguments are usable for input, in the normal way that function arguments are, and their values can be used within the function's definition by writing
., just like function arguments in more normal languages. I don't think any of Brachylog's quirks change our standard consensus for how function arguments work.
- We can take output via standard output, because legal output methods for full programs are also legal for functions. This means that
w is an acceptable form of output from Brachylog predicates (which is definitely handy if we're already using both the arguments we have as inputs!)
- We can produce output via assigning/mutating the function arguments, in languages where the function's caller will be able to see the assigned values. Unifying the arguments with a value is similar enough to assigning to them (both operations set the value, and the only relevant difference with unification is that you can't subsequently overwrite the value, which doesn't matter here). This allows for idiomatic output via the Output argument
., which is very common in Brachylog (mostly because the syntax does this by default). I can't see any reason, other than convention (and when did code-golf submissions ever feel bound by convention?), that it shouldn't be used to output via the Input either; given that it's acceptable to output via the inputs on PPCG in languages like C (where functions have an actual, defined, return value that can't be made to take input), it should also be acceptable to output via the Input on PPCG in languages like Brachylog (where there's no concept of a return value, but one of the arguments is normally used as a return value by convention).
- We might or might not be able to produce output via whether the predicate fails or succeeds. At the time of writing, I'd only just added this to the I/O defaults post (as I realised it was missing as a consequence of writing this post, and it's a fairly natural way to output a boolean in declarative languages); as such, there isn't a consensus on whether this is legal or not. However, whatever consensus is found in the I/O defaults post, it should apply to Brachylog too; a failure in a declarative language is very similar to an exception in a language like OCaml (I picked OCaml because using exceptions for flow control is considered acceptable by its community, as opposed to languages like Java where doing so is typically avoided).
In short, my opinion is this: we have general-purpose rules for what forms of I/O are legal, and they apply pretty much directly to Brachylog, so we don't need a special-cases rule. They're fairly permissive, but that makes sense, because they're fairly permissive in other languages too.
Brachylog may have conventions to use function arguments and program input and arguments in a certain way, but in general, we tend to ignore those sorts of conventions on PPCG, and they shouldn't override our general rule. It isn't conventional to output from a function via using
print in Python, but it's very common on PPCG as it's a byte shorter than
return. So given that we have an established consensus on how I/O works in general, why not use it here too?