Groovy named positional argument with default value - groovy

I have this code:
def myFunc(arg1, arg2="default arg2", arg3="default arg3") {
println("arg1: ${arg1}")
println("arg2: ${arg2}")
println("arg3: ${arg3}")
}
myFunc(1, arg3="foo")
My desired output:
arg1: 1
arg2: default arg2
arg3: foo
Actual output:
arg1: 1
arg2: foo
arg3: default arg3
I want to override the last argument without changing the middle arg2 which has a default value I need.
I cannot use classes.
What are my options?

This is not possible in groovy, The right most optional parameter is dropped first and so on and so forth.
Better explanation:
https://mrhaki.blogspot.com/2009/09/groovy-goodness-parameters-with-default.html
The implementor would need to pass in the default 2nd param when invoking this method.

As already answered, this does not work - groovy has no named arguments.
One thing people often confuse for named arguments, is the passing of
maps. E.g. you can have the defaults be merged with the arguments and
come close to what you are after:
def myFunc(Map kwargs = [:]) {
def args = [arg1: "a1", arg2: "a2", arg3: "a3"].tap{putAll(kwargs)}
println([args.arg1, args.arg2, args.arg3])
}
myFunc(arg3: "new a3")
// => [a1, a2, new a3]
You can also check for the incoming keys beforehand, that they are in
the default to notify the consumer at least at runtime, that they are
sending unknown keys.

Related

Reading class method definition - what is Callable?

I am relatively new to python, and I started to read the docs when using packages, but I'm having a hard time understanding some of it:
post_to_main_thread(self: open3d.cpu.pybind.visualization.gui.Application, arg0: open3d::visualization::gui::Window, arg1: Callable[[], None]) → None
the only thing here that I don't understand is the arg1 with that callable, and I can't find an explanation on it at the web.
Interesting question!
So post_to_main_thread() is a method that takes 3 arguments (inputs/variables) and returns None.
Because it's a method (a function associated with a class, not just a standalone function) the first argument, self, refers to the instance of the class that the function is part of.
The other two arguments are passed within the function parentheses, as expected with a standalone function. So a call might look like this:
instance_name = open3d.visualization.gui.Application(...)
instance_name.post_to_main_thread(arg1, arg2)
arg1 should be of type open3d::visualization::gui::Window. This is an instance of the class open3d.visualization.gui.Window().
arg2 should be of type Callable(). This describes a number of built-ins that you can find details about in the documentation. To quote:
The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type.
So in this case the type should be Callable[[], None], which means this should be a function that takes no input and returns None. Carrying on from our previous example, you'd pass this as an argument like so:
def my_callable:
print('Hello, World!')
return
instance_name.post_to_main_thread(arg1, my_callable)
Does that clear things up?

Can __rrshift__ take more than one argument?

I am trying to overload the __rrshift__ method on one class. The basic implementation follows:
class A:
def __rrshift__(self, arg0):
print(f'called A.__rrshift__ with arg0={arg0}')
a = A()
42 >> a
Here I got the expected result:
called A.__rrshift__ with arg0=42
But if I call this method with more than one argument, only the last one is used:
'hello', 'world' >> a
Which returns:
called A.__rrshift__ with arg0=world
If I try and add arguments to the __rrshift__ method, the behavior is not modified as I expected:
class B:
def __rrshift__(self, arg0, arg1=None):
print(f'called B.__rrshift__ with arg0={arg0}, arg1={arg1}')
b = B()
42 >> b
'hello', 'world' >> b
# called B.__rrshift__ with arg0=42, arg1=None
# called B.__rrshift__ with arg0=world, arg1=None
Is it possible to consider more than one argument for the __rrshift__ method?
I'm afraid that's not possible.
__rrshift__, like __add__, __sub__ et al. are binary operators. They accept exactly two arguments: self and whatever_other_argument.
Of course, you can cheat the system by calling these methods explicitly, and then they'll be able to accept as many arguments as you want, but if you use the operators like >>, +, - et al., then the syntax of the language will force them to accept two arguments exactly.
You can probably modify that by hacking the heck of Python's grammar with the ast module, but that won't be Python anymore.
Here's how a, b >> c is seen by the Python parser, according to the grammar:
>>> ast.dump(ast.parse('a, b >> c'))
# I prettified this myself. The actual output of `dump` is horrendous looking.
Module(
body=[
Expr(
# Build a tuple...
value=Tuple(elts=[
Name(id='a', ctx=Load()), # ...containing `a`...
# ...and the result of a BINARY OPERATOR (RShift)...
BinOp(
left=Name(id='b', ctx=Load()), # ...which is applied to `b`...
op=RShift(),
right=Name(id='c', ctx=Load()) # ...and `c`
)
],
ctx=Load()
)
)
]
)
The production in the grammar that produces [sic] the tuple seems to be the following:
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
As you can see, it then goes on to parser the test production, which is then unpacked all the way to the expr production, which then arrives at the following production:
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
So, the first test in testlist_star_expr resolves to atom: NAME, and the second one - to shift_expr. This later ends up constructing the tuple.
I am not sure what you are after but if you just need to supply some args (kwargs eventually), this might show you how to achieve that:
class A:
def __rrshift__(self, *args):
if len(args) > 1:
print(f'called A.__rrshift__ with arg={", ".join(args)}')
else:
print(f'called A.__rrshift__ with arg={args[0]}')
a = A()
a.__rrshift__('hello', 'world')
a.__rrshift__(42)
#called A.__rrshift__ with arg=hello, world
#called A.__rrshift__ with arg=42

How to avoid casting arguments in Spock

I want to get a List from repository and assert its contents.
In following code I get a warning that states that Object cannot be assigned to List
Is there any way to add better argument to handle such case?
myDomainObjectRepository.save(_) >> { arguments ->
final List<MyDomainObject> myDomainObjects = arguments[0]
assert myDomainObjects == [new MyDomainObject(someId, someData)]
}
To elaborate on Opals answer: There are two parts and a footnote in the docs that are relevant here:
If the closure declares a single untyped parameter, it gets passed the
method’s argument list:
And
In most cases it would be more convenient to have direct access to the
method’s arguments. If the closure declares more than one parameter or
a single typed parameter, method arguments will be mapped one-by-one
to closure parameters[footnote]:
Footnote:
The destructuring semantics for closure arguments come straight from
Groovy.
The problem is that you have a single argument List, and since generics are erased groovy can't decide that you actually want to unwrap the list.
So a single non-List argument works fine:
myDomainObjectRepository.save(_) >> { MyDomainObject myDomainObject ->
assert myDomainObject == new MyDomainObject(someId, someData)
}
or a List argument combined with a second, e.g., save(List domain, boolean flush)
myDomainObjectRepository.save(_, _) >> { List<MyDomainObject> myDomainObjects, boolean flush ->
assert myDomainObjects == [new MyDomainObject(someId, someData)]
}
So the docs are a little bit misleading about this edge case. I'm afraid that you are stuck with casting for this case.
Edit: You should be able to get rid of the IDE warnings if you do this.
myDomainObjectRepository.save(_) >> { List<List<MyDomainObject>> arguments ->
List<MyDomainObject> myDomainObjects = arguments[0]
assert myDomainObjects == [new MyDomainObject(someId, someData)]
}
The docs seems to be precise:
If the closure declares a single untyped parameter, it gets passed the method’s argument list
However I've just changed my spec that uses rightShift + arguments to accept a single type argument and it did work. Try it out.

Pharo method with multiple arguments

I am creating a Pharo Class method that takes 3 arguments. I am using the following code and it gives me the error "Variable or expression expected.."
MethodName: arg1:argValue1 arg2:argValue2
^ self var1: argValue1 var2: self var3: argValue2
What would be the correct method declaration syntax? Later on, I intend on calling this method like below :
ClassName var1: argValue1 var2: self var3: argValue2
The bit that you have to understand when it comes to naming methods in Smalltalk is that the method can be split into multiple parts, delimited by colons (:), and that the arguments are inserted after each of those colons. Semantically, that makes a lot of sense, and allows you to use good naming practices so that your code reads almost like an English sentence (which is why the language was named Smalltalk).
So for a method that, in Java or a similar "curly bracket language", might look something like this:
registerUser(String emailAddress, String password, boolean isAdmin) {...}
you would split up the method name in the declaration to fit the arguments, like this:
registerUserWithEmail: anEmailAddress password: aPassword isAdmin: aBoolean
making the method name (often prefixed with # in Smalltalk because it is registered as a "symbol" in a global dictionary):
#registerUserWithEmail:password:isAdmin:
That whole thing is the method name, and when calling it, you'd insert the appropriate arguments after the colons (let's assume this method is defined in the class UserRegistry):
UserRegistry
registerUserWithEmail: 'joe#bloggs.com'
password: 'topSecret'
isAdmin: true
Of course you can leave all that on one line, depending on its length and your readability preferences.
Getting back to your case, you had this:
MethodName: arg1:argValue1 arg2:argValue2
The problem to which your compiler is trying to alert you when it says "Variable or expression expected" is that there's nothing between MethodName: and arg1:. I think you may have assumed that MethodName: wasn't part of the method name but part of the definition.
Some notes:
by convention, method names use lowerCamelCase
#methodName:arg1:arg2: wouldn't make a very good method name in Smalltalk, because the name should describe what it does and, when arguments come into play, what arguments are expected; the same goes for #var1:var2:var3:
if you're going to be calling this method by sending a message to ClassName, then that method needs to be defined on the class side of ClassName, not on the instance side
think about what you're calling/passing in your example - unless you're doing something complicated with a hierarchy of objects, there may not be much point in sending a message to self with one of the arguments being self as well; ask yourself whether you can simplify something there (hard to be more concrete without knowing what you're trying to do)
What you wrote is correct, but MethodName is extra and then your method would look like this:
arg1: argValue1 arg2: argValue2
^ self var1: argValue1 var2: self var3: argValue2
Which is a 2 arg method. You can write 3 arg method in a same way:
arg1: argValue1 arg2: argValue2 arg3: argValue3
^ self var1: argValue1 var2: self var3: argValue2
Then what you are doing is calling another 3 arg class method #var1:var2:var3: with arguments: argValue1, self, argValue2 (I hope this is your intension because this looks weird).

DSL with groovy -> passing values to metaClass methods

I'm trying to write a mini DSL for some specific task. For this purpose I've been trying to solve a problem like this below (without using parantheses):
give me 5 like romanLetter
give me 5 like word
where the first line would return "V" and the second "five"
My definitions for the first part give me 5 look like this
def give = { clos -> clos() }
def me = { clos -> [:].withDefault { it
println it}
}
and then give me 5 prints 5
The problem is how to add more metaclass methods on the right. E.g.
give me 5 like romanLetter -> prints V OR
give me 5 like word -> prints five
my intuition is that I define like as
Object.metaClass.like = {orth -> if (orth.equals("roman")){ println "V"}
else {println "five"} }
this metaClass method like works only if there is a returned value from the left to be applied to, right? I tried adding a return statement in all of the closures which are on the left side but I always receive
groovy.lang.MissingPropertyException: No such property: like
for class: com.ontotext.paces.rules.FERulesScriptTest ...
do you have an idea how shall I do?
========================================
Here is the application of what I'm asking for.
I want to make a rule as follows
add FEATURE of X opts A,B,C named Y
where add is a closure, of, opts and named are MetaClass methods (at least that's how i imagine it), X, A, B, C, Y are parameters most probably strings and FEATURE is either a MetaClass property, or a closure without arguments or a closure with arguments.
If FEATURE does not take arguments then it is enough that add takes FEATURE as argument and returns a value on which
Object.metaClass.of will be executed with parameter X
Object.metaClass.opts will be executed on the returned by OF value with parameters A, B, C
Object.metaClass.named will be executed on the returned by opts value with parameter Y
each one of these metaclass methods sets its parameter as a value in a map, which is passed to a JAVA method when named is called.
I'm not sure this is the best solution for such a problem, but it seems to me such for the moment. The problem is if FEATURE is not a property itself but a closure which takes argument (e.g. feature1 ARG1). Then
add feature1 ARG1 of X opts A,B,C named Y
and this is the case which I'm stuck with. add feature1 ARG1 is the give me 5 part and I'm trying to add the rest to it.
========================================================
EXAMPLES:
I need to have both of the following working:
add contextFeature "text" of 1,2,3 opts "upperCase" named "TO_UPPER"
add length named "LENGTH"
where in the first case by parsing the rule, whenever each metaclass method of, opts, named is called I fill in the corresponding value in the following map:
params = [feature: "text",
of: 1,2,3,
opts: "upperCase",
named: "TO_UPPER"]
ones this map is filled in, which happens when named is parsed, I call a java method
setFeature(params.of, params.named, params.opts, params.feature)
In the second case length is predefined as length = "length", params values will be only
params = [feature : length,
of: null,
opts: null,
named: "LENGTH"]
and since of is null another java method will be called which is addSurfaceFeature(params.feature, params.named). The second case is more or less streight forward, but the first one is the one I can't manage.
Thanks in advance! Iv
You can do this sort of thing... Does that get you close?
def contextFeature( type ) {
"FEATURE_$type"
}
// Testing
new IvitaParser().parse {
a = add text of 1,2,3 opts "upperCase" named "TO_UPPER"
b = add length named "LENGTH"
c = add contextFeature( "text" ) of 1,2,3 opts "upperCase" named "TO_UPPER"
}
assert a == [feature:'text', of:[1, 2, 3], opts:'upperCase', named:'TO_UPPER']
assert b == [feature:'length', of:null, opts:null, named:'LENGTH']
assert c == [feature:'FEATURE_text', of:[1, 2, 3], opts:'upperCase', named:'TO_UPPER']
// Implementation
class IvitaParser {
Map result
def parse( Closure c ) {
c.delegate = this
c.resolveMethod = Closure.DELEGATE_FIRST
c()
}
def propertyMissing( String name ) {
name
}
def add( String param ) {
result = [ feature:param, of:null, opts:null, named:null ]
this
}
def of( Object... values ) {
result.of = values
this
}
def named( String name ) {
result.named = name
result
}
def opts( String opt ) {
result.opts = opt
this
}
}
You can even get rid of the quotes on the definition:
a = add text of 1,2,3 opts upperCase named TO_UPPER
b = add length named LENGTH
As the propertyMissing method just converts unknown properties into a String of their name

Resources