How can we specify the attributes of a Callable argument in a subroutine - signature

This comes from this perl6/doc issue which also refers to these questions in the IRC channel
The documentation specifies how to constrain the arguments of a callable using a Signature literal:
sub f(&c:(Int, Str)) { say c(10, 'ten') };
(this would restrict the function argument to only those that take an Integer and a String as an argument).
However, in some other situations, where constraining can be used, for instance if you need to restrict the arity or the return values. However, is there any simpler way or syntax of doing this?

To enforce an arity of, for example, 2, then a signature literal can also be used:
sub foo(&x:($,$)) {
x(1,2)
}
Then this works:
foo -> $a, $b { say $a + $b }
While this dies:
foo -> $a { say $a }
This signature literal just means "any two arguments". The return type can also be constrained by similar means:
sub foo(&x:(--> Int)) {
say x()
}
Meaning this works:
foo sub (--> Int) { 42 }
But this dies:
foo sub () { "oops" }

Related

Spock reusing generic closure

I have this code in Spock:
then:
1 * dao.getByValue(Something.ONE, _ as String) >> {Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
It doesn't look exactly like that, but you get the point. I want to return an object based on arguments passed to the method, in the real version this object is loaded from database.
The point is that I have this call in a lot of places and it looks exactly the same everywhere. Could I somehow extract this closure and use it everywhere, like this:
then:
1 * dao.getByValue(Something.ONE, _ as String) >> Closures.makeSomething
I tried using Intellij extract feature, but it kinda went crazy there with types, after I edited the types manually I had weird errors:
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'mypackage.MySpec$__clinit__closure1#1757cd72' with class 'mypackage.MySpec$__clinit__closure1' to class 'java.util.Optional'
Even that one didn't work, and I thought it would:
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> {args -> makeSomething.call(args[0], args[1]) }
groovy.lang.MissingMethodException: No signature of method: mypackage.MySpec$__clinit__closure2.call() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[mypackage.Something$$Lambda$6/1105423942#6f45df59, ...]]
I'm not good at Groovy or Spock in general, I'm just trying this out for now.
Edit:
Working code after #tim_yates suggestion (whole interaction is in the helper method):
then:
interaction {
somethingCall(2, Something.TWO)
somethingCall(3, Something.ONE)
}
}
private void somethingCall(int times, Something something) {
times * dao.getByValue(something, _ as String) >> { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
}
Not working code that I'd like (only the return value is in the helper method):
then:
2 * dao.getByValue(Something.TWO, _ as String) >> makeSomething
3 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
}
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
If I simply inline each >> makeSomething and write there it's body instead, then it works.
You have a conceptual problem here. You cannot just split the closure from the preceding code because if you look at it
dao.getByValue(something, _ as String) >> { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
you might notice that smth and value inside the closure get their values from getByValue(something, _ as String).
Now if you factor out the in-line closure part into a stand-alone closure instance, you lose that connection. First of all, >> makeSomething has no parameters, secondly you do not evaluate the makeSomething closure, i.e. on the right hand side you do not get your Optional instance but a Closure instance. In order to evaluate the closure you have to call it with parameters, i.e. something like >> makeSomething(something, "dummy") would work. But this way you have to repeat the first getByValue parameter and make up a dummy for the second, unspecified one because you have no easy way to refer to it other than introducing yet another closure like >> { Something smth, String value -> makeSomething(smth, value) }. But then you are not saving a lot of code.
It is your decision if this is nicer than somethingCall(2, Something.TWO) (I like it, actually) or if you go for my contrived construct. What I cannot do for you is change Groovy or Spock DSL syntax just because you prefer it to look different.

Does an anonymous parameter in a Perl 6 signature discard the value?

In the Perl 6 Signature docs, there's an example of an anonymous slurpy parameter:
sub one-arg (#) { }
sub slurpy (*#) { }
one-arg (5, 6, 7); # ok, same as one-arg((5, 6, 7))
slurpy (5, 6, 7); # ok
slurpy 5, 6, 7 ; # ok
There are no statements in the subroutine, mostly because the text around this is about the parameter list satisfying the signature rather than what the subroutine does with it.
I was playing with that and trying to make a subroutine that takes a list of one of more items (so, not zero items). I didn't particularly care to name them. I figured I'd still have access to the argument list in #_ even with the signature. However, you get #_ when you don't have a signature:
$ perl6
To exit type 'exit' or '^D'
> sub slurpy(*#) { say #_ }
===SORRY!=== Error while compiling:
Placeholder variable '#_' cannot override existing signature
------> sub⏏ slurpy(*#) { say #_ }
Is there another way to get the argument list, or does the anonymous parameter discard them? I see them used in the section on type constraints, but there isn't an example that uses any of the parameter values. Can I still get the argument list?
The values aren't discarded; you can for example access it through nextsame:
multi access-anon(*#) is default {
say "in first candidate";
nextsame;
}
multi access-anon(*#a) {
#a;
}
say access-anon('foo')
Output:
in first candidate
[foo]
But to get to your original objective (an array with at least one element), you don't actually need to access the list; you can use a sub-signature:
sub at-least-one(# [$, *#]) { }
at-least-one([1]); # no error
at-least-one([]); # Too few positionals passed; expected at least 1 argument but got only 0 in sub-signature
Does an anonymous parameter in a Perl 6 signature discard the value?
Yes, unless you capture an argument using some named parameter in the function's signature, it won't be available in the function's body. (PS: They're not literally discarded though, as moritz's answer shows.)
I figured I'd still have access to the argument list in #_ even with the signature.
#_ is not an alternative to using parameters - it is a parameter.
Every function has a well-defined signature that specifies its parameters, and they represent the only mechanism for the function body to get at the values the function is passed.
It's just that there are three different ways to declare a function signature:
If you explicitly write out a signature, that's what is used.
If you use placeholder parameters (#_ or $^foo etc.) in the body of the function, the compiler uses that information to build the signature for you: say { $^a + #_ }.signature; # ($a, *#_)
If neither of the above is the case, then the signature becomes:
In case of subroutines, one that accepts zero arguments: say (sub { 42 }).signature; # ()
In case of bare blocks, one that accepts zero or one argument available as $_: say { 42 }.signature; # (;; $_? is raw)
In all cases, the function ends up with an unambiguous signature at compile time. (Trying to use $^foo in a function that already has an explicit signature, is a compile-time error.)
Is there another way to get the argument list
Make sure to capture it with a non-anonymouys parameter. If you want it to be accessible as #_, then call it that in the explicit signature you're writing.
I was [...] trying to make a subroutine that takes a list of one of more items
You can use a sub-signature for that:
sub foo (*#_ [$, *#]) { ... };
Or alternatively a where constraint:
sub foo (*#_ where .so) { ... };
To have #_ populated, the signature either has to be implicit or explicit, not both.
sub implicit { say #_ } # most like Perl 5's behaviour
sub explicit ( *#_ ) { say #_ }
sub placeholder { $^a; say #_ }
This applies to blocks as well
my &implicit = { say #_ }
my &explicit = -> *#_ { say #_ }
my &placeholder = { $^a, say #_ }
Blocks can also have an implicit parameter of $_, but #_ takes precedence if it is there.
{ say $_ }(5) # 5
$_ = 4;
{ #_; say $_ }(5) # 4
It makes sense to do it this way because one programmer may think it works the way you think it does, or that it is slurpy like it would be if implicit, and another may think it gets all of the remaining arguments.
sub identical ( #_ ) { say #_ }
sub slurpy ( *#_ ( #, *# ) ) { say #_ } # same as implicit
sub slurpy2 ( **#_ ( #, *# ) ) { say #_ } # non-flattening
sub remaining ( #, *#_ ) { say #_ }
identical [1,2]; # [1 2]
slurpy $[1,2],3,4,5; # [[1 2] 3 4 5]
slurpy2 [1,2],3,4,5; # [[1 2] 3 4 5]
remaining [1,2],3,4,5; # [3 4 5]
#_ may also be added as a mistake, and in that case it would be preferable for it to produce an error.
There is no way to get at the raw arguments without declaring a capture parameter.
sub raw-one ( |capture ( # ) ) { capture.perl }
sub raw-slurpy ( |capture, *# ) { capture.perl }
raw-one [1,2]; # \([1, 2])
raw-slurpy 1,2 ; # \(1, 2)

Combine multiple generic types in hacklang

I am trying to implement the reduce function from underscore in hack. In underscore, the reduce function has the following behavior:
If no memo is passed to the initial invocation of reduce, the iteratee is not invoked on the first element of the list. The first element is instead passed as the memo in the invocation of the iteratee on the next element in the list.
My attempt to implement the function:
function reduce<T, Tresult>(
Iterable<T> $iterable,
(function(?Tresult, T):Tresult) $fn,
?Tresult $memo=null):?Tresult {
if (is_null($memo)) {
$memo = $iterable->firstValue();
$iterable = $iterable->skip(1);
}
foreach ($iterable as $value) {
$memo = $fn($memo, $value);
}
return $memo;
}
This results in the error:
Invalid return type (Typing[4110])
This is a value of generic type Tresult
It is incompatible with a value of generic type T
via this generic Tv
How do I tell the type checker that T == Tresult when is_null($memo)
I note that the line
$memo = $iterable->firstValue();
assigns a value of type T to $memo. This seems wrong; $memo is given to be of type ?Tresult in the declaration, and assigned a value of type Tresult here:
$memo = $fn($memo, $value);
Can you explain why $memo is assigned a value of type T in the first instance? How do you know that T and Tresult are the same? I see no evidence whatsoever that these two types are ever constrained to be the same thing. The type checker is giving you an error here because this program isn't typesafe; if T is Animal and Tresult is Fruit, and someone passes in a null fruit, there's no way to get a fruit out of the sequence.
Also, I find it weird that reduce returns a nullable result; surely it should be returning a result of the given result type, no?
If you want this function to have two different behaviours depending on the nullity of the argument, then why not instead simply have two functions?
function reduce1<T, Tresult>(
Iterable<T> $iterable,
(function(Tresult, T):Tresult) $fn,
Tresult $memo): Tresult {
foreach ($iterable as $value) {
$memo = $fn($memo, $value);
}
return $memo;
}
function reduce2<T>(
Iterable<T> $iterable,
(function(T, T):T) $fn): T {
return reduce1($iterable->skip(1), $fn, $iterable->firstValue());
}
There, now we have two different forms of reduce, and both of them are typesafe.

Problems overloading Groovy comparison operators

I am building an analytic application using Groovy and require very forgiving math operators regardless of data format. I achieve this through operator overloading, in many cases improving (in my case) on the default Groovy type flexibility. As an example, I need 123.45f + "05" to equal 128.45f. By default Groovy downgrades to String and I get 123.4505.
In most cases my overloading works very well, but not for comparison operators. I've followed a couple of discussions on this, but I'm not getting to an answer and I'm looking for ideas. I recognize that the goal is to overload compareTo() (vs. something like lessThan), but Groovy seems to ignore this and instead attempts its own smart comparison - e.g. DefaultTypeTransformation.compareTo(Object left, Object right), which fails on mixed types.
Unfortunately this is a must have for me, because improperly comparing two values compromises the whole solution and I don't have control over some of the data types being analyzed (e.g. vendor data structures).
For example, I need the following to work:
Float f = 123.45f;
String s = "0300";
Assert.assertTrue( f < s );
I have many permutations of these, but my attempt to overload includes (let's just assume my JavaTypeUtil does what I need if I can get Groovy to call it):
// overloads on startup, trying to catch all cases
Float.metaClass.compareTo = {
Object o -> JavaTypeUtil.compareTo(delegate, o) }
Float.metaClass.compareTo = {
String s -> JavaTypeUtil.compareTo(delegate, s) }
Object.metaClass.compareTo = {
String s -> JavaTypeUtil.compareTo(delegate, s) }
Object.metaClass.compareTo = {
Object o -> JavaTypeUtil.compareTo(delegate, o) }
When I try the above test, none of these are called and instead I get:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Float
at java.lang.Float.compareTo(Float.java:50)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareToWithEqualityCheck(DefaultTypeTransformation.java:585)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareTo(DefaultTypeTransformation.java:540)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo(ScriptBytecodeAdapter.java:690)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan(ScriptBytecodeAdapter.java:710)
at com.modelshop.datacore.generator.GroovyMathTests.testMath(GroovyMathTests.groovy:32)
Debugging through I see that the < operator goes right to ScriptBytecodeAdapter.compareLessThan(), and the implementation of that seems to ignore the overloaded compareTo() here:
In DefaultTypeTransformations.java:584 (2.4.3)
if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass())
|| (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
|| (left instanceof GString && right instanceof String)) {
Comparable comparable = (Comparable) left;
return comparable.compareTo(right); // <--- ***
}
In a desperate attempt, I've also tried to overload compareLessThan, but I'm grasping now, I don't know that there's any way to jump in front of the < mapping in Groovy.
Float.metaClass.compareLessThan << {
Object right -> JavaTypeUtil.compareTo(delegate, right) < 0 }
Float.metaClass.compareLessThan << {
String right -> JavaTypeUtil.compareTo(delegate, right) < 0 }
Any thoughts on a work-around? Thanks!
Part of it is you need to include static, like this
Float.metaClass.static.compareTo = { String s -> 0 }
This makes f.compareTo(s) work, but the < operator still won't work. This is a known limitation. The only operators that can be overloaded are mentioned in the documentation. Possibly you could do a custom AST to change all those operators to a compareTo().
But this isn't the whole story. f <=> s also doesn't work, despite <=> delegating to compareTo(). I believe this is because Float doesn't implement Comparable<Object> or Comparable<String>, only Comparable<Float>. Although I'm not sure where exactly in the chain Groovy makes the decision not to use that method, you can see it's not limited to Groovy's math classes. This also doesn't work
Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''
class Foo implements Comparable<Foo> {
int compareTo(Foo o) {
0
}
}
I think Groovy is doing some pre-parsing validation that's preventing the metaclass stuff from working. Whatever validation it's doing definitely inspects the interfaces implemented, because this fails for a different reason
Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''
class Foo {
int compareTo(Foo o) {
0
}
}
In both of these examples, replacing <=> with compareTo() works.
This question has been asked a couple times before, but I haven't seen a good explanation for why. You might try asking on the user mailing list. I'm sure Jochen or Cedric would be able to explain why.
I guess the point is that your compareTo closure is expecting an Object; so when you invoke compareTo with a String, your closure doesn't get called at all.
I can only think of the following; being precise when specifying closure input parameter type:
Float.metaClass.compareTo = { Integer n -> aStaticHelperMethod(n) }
Float.metaClass.compareTo = { String s -> aStaticHelperMethod(s) }
Float.metaClass.compareTo = { SomeOtherType o -> aStaticHelperMethod(o) }

TCL use "eq" or "==" when the argument can be a list or a string

I have the following if loop:
if { expr { $Node_3 eq "BadAngle" } } {
return "BadAngle"
}
Node_3 can be a list containing coordinates, or the simple string "BadAngle"
I use TCLPro 1.4 for debugging and TCL 8.6. I get:
*syntax error in expression " expr { $Node_3 eq "BadAngle" } "*
I then also tried:
if { [ expr { $Node_3 eq "BadAngle" ] == 1 } } {
return "BadAngle"
}
But i get the same error.
Also: What is the better alternative in this case: To use "==" or "eq", I think "eq" because a list is a kind of a string..or?
You seem to be getting in a bit of a pickle there. Firstly, you probably don't want to use expr inside the if condition, so this will be enough:
if { $Node_3 eq "BadAngle" } {
return "BadAngle"
}
Since one of the things you are comparing against is definitely non-numeric, you'll be fine using the eq operator though the == operator is equivalent. The only time there is a difference is when both operands look like numbers, when == will compare them as numbers (parsing them into numbers if necessary). This means that "1" == "0x01" despite them being rather different strings; the eq operator always compares as strings.
How to choose which is best? It's actually pretty easy: do you think you are working with numbers at this point on both sides of the comparison? If yes, use ==, and otherwise use eq.
The one time you want to use expr inside if is when you're dynamically selecting the operator. This is not really recommended, but you'd do it like this:
set op "=="
if { [expr {$Node_3} $op {"BadAngle"} ]} {
return "BadAngle"
}
It's pretty ugly. (Notice that I put everything else inside its own braces to prevent double evaluation, and I'm careful with balancing all the brackets correctly, which is what was tripping you up in the code in your question.) Don't do it unless you really really need to.
You have two distinct problems in your attempts:
In the first example you're trying to call expr in a wrong way: if (and other commands which support conditions, such as while) use the same machinery to evaluate their condition the expr command does — to cite the manual:
The if command evaluates expr1 as an expression (in the same way that expr evaluates its argument).
Now that machinery sees a bare word "expr" and has no idea what to do with it.
There are two ways to fix the code:
Wrong way — properly call expr:
if {[expr {$Node_3 eq "BadAngle"}]} { ... }
This is wrong because the nested call to expr is purely superficial.
Correct way — just drop unneeded expr:
if {$Node_3 eq "BadAngle"} { ... }
In the second example you decided to make the situaton more complicated by introducing another boolean test (not needed as the eq and == operators already return a value of type boolean). But in this case you forgot to put the closing curly brace, }, which performs grouping, for the code should have been
if { [ expr { $Node_3 eq "BadAngle" } ] == 1 } } { ... }
In any case the correct solution is to simply use
if {$Node_3 eq "BadAngle"} { ... }

Resources