Pharo method with multiple arguments - pharo

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).

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?

Get Parameter list of a closure dynamically in Groovy

I have a Closure defined in a groovy file that load with the shell.evaluate() method.
I need to call this closure in by calling program using the shell."$closurename".call(arguments) call.
However to formulate the closure parameters ( argument above) I'd need to now what are the arguments and arguments names that the closure $Closurename accepts. Is there a way of dynamically knowing this in Groovy? I checked in the metaClass.method property but this does not work in my example below.
Below is the example code.
def arguments;
shell.evaluate(new File("/tmp/myGroovyClosureFile.groovy"))
testBlock = "myClosureName"
//Code here to find the parameters for myClosureName and create
//the arguments variable
shell."$testBlock".call(arguments)
As Jeff mentioned, it seems like groovy when generating code for closures anonymizes somehow the parameter names. However, you can still use reflection to get as much information as you can:
def cl = { int a, String b ->
println(a)
println(b)
}
def callMethod = cl.class.methods.find {
it.name == "call"
}
println callMethod.parameterTypes
println callMethod.parameters.name
and outputs:
[int, class java.lang.String]
[arg0, arg1]
Is there a way of dynamically knowing this in Groovy?
You can't really do it dynamically at runtime.

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.

Xquery ( XDMP-ARGTYPE error ) the expected type of a function input is different from the actual type

I'm trying to remove stop words from a text in MarkLogic 8 using this function :
declare function rec:remove-stop-words($string, $stop_words) {
(: This is a recursive function. :)
if(not(empty($stop_words))) then
rec:remove-stop-words(
replace($string, $stop_words[1], '', 'i'),
(: This passes along the stop words after
the one just evaluated. :)
$stop_words[position() > 1]
)
else normalize-space($string)
};
Here where I call it
for $r in /rec:Record
return
rec:remove-stop-words(data($r/rec:Abstract), $stop_words}
It gives me the following error
XDMP-ARGTYPE: (err:XPTY0004) fn:replace((xs:untypedAtomic("
chapter
utilized asymmetry of n..."), xs:untypedAtomic("
book
interrelationship between ...")), "a", "", "i") -- arg1 is not of type xs:string?
The function expects a string type but the actual type is untypedAtomic. I don't know what to do!
NOTE: (( The problem is not in the function because I've tried to use it for a different text and it worked well )).
I tried to the code by converting untypedAtomic to string by:
return
<info>{rec:remove-stop-words(data(xs:string($r/rec:Abstract)), $stop_words)}</info>
but I got this error:
XDMP-ARGTYPE: (err:XPTY0004) fn:replace(("
chapter
utilized asymmetry of n...", "
book
interrelationship between ..."), "a", "", "i") -- arg1 is not of type xs:string
The problem is that when you iterate over /rec:Record and pass $r/rec:Abstract as input, at least one of your records is returning more than one rec:Abstract. The function signature for rec:remove-stop-words allows a sequence of values as input for $string, but the function body where you call fn:replace only handles input for a single value, so it throws an argument exception (given xs:string+ and expecting xs:string?).
You can handle the sequence by iterating over rec:Abstract before you call the function:
for $r in /rec:Record
for $a in $r/rec:Abstract
return
rec:remove-stop-words($a, $stop_words)
If you use stricter function signatures, it can help avoid problems like this, or at least make them easier to debug. For example, if you define your function to only allow a single input for the first parameter:
rec:remove-stop-words($string as xs:string, $stop_words as xs:string*)
...
This will throw a similar exception when $string is passed a sequence, but higher up the call stack, which can help make these types of errors a little more obvious.
Try using this code -
for $r in /rec:Record
return
rec:remove-stop-words(fn:string($r/rec:Abstract), $stop_words}
It looks like you are sending it a node and not a string. Try $r/rec:Abstract/text() or $r/rec:Abstract/string()

Passing a def variable to a typed BigDecimal argument in Groovy

I am new to Groovy so I am a bit confused by the run time binding, typed and not typed attributes of the language. Personally I prefer types to be declared.
However, I have a question.
I have a small method that takes some variable from maps, input, whatever, that I know are numbers. Let's say that I don't know what the initial type was (it's somewhere deep in the code or comes from an external source), other that it was a number. Now I have a method that takes two of these arguments and I have to do a modulo operation on them. Because they might be decimal or not, I wrote a small method using the remainder of BigDecimal so to enforce the type I used the type BigDecimal on the method signature.
def callerMethod(Map map) {
...
map.each{
calledMethod(it.val1, it.val2)
...
}
...
}
def calledMethod(BigDecimal val1, BigDecimal val2) {
...
vl1.remainder(val2)
...
}
Is this correct? If the incoming argument is Integer (most of the time the primitives are boxed if I understand it correctly), will it be implicitly cast or turned into a BigDecimal?
How does this work in Groovy.
I still think that since I have the option to use types, I want to use them rather than declaring everything def. It also makes it easier to read code or see what something is if you reading already existing code
The problem in this methods are not the type of variables, is the each of your map
In a groovy Map, the each have two signatures.
One receive a Map.Entry of parameter and other receive key and value
Ex.:
Map map = [key1:'value1',key2:'value2']
map.each{ Map.Entry entryMap ->
println "The value of key: ${entryMap.key} is ${entryMap.value}"
}
The result of this each will be:
The value of key: key1 is value1
The value of key: key2 is value2
Or could be like this
Map map = [key1:'value1',key2:'value2']
map.each{ def key, def value ->
println "The value of key: ${key} is ${value}"
}
And the result of this second will be the same of the first.
If you want to pass two specific arguments to you calledMethod, pass both outside of the each like this:
def callerMethod(Map map) {
calledMethod(map.val1, map.val2)
}
I don't understand perfectly what you want.. I hope that's help you to do you code.

Resources