Create GString from String - groovy

We're using groovy in a type-safe way. At some point I want to invoke a method with signature
void foo(GString baa)
As long the String I enter contains some ${baz} everything is fine, but when I use a pure String I get a compile error
foo("Hello, ${baz}") // fine
foo("Hello, world") // Cannot assign value of type java.lang.String to variable of type groovy.lang.GString
foo("Hello, world${""}") // fine but ugly
Is there a nice way to create a GString out of String?
EDIT
Guess I've oversimplicated my problem. I'm using named constructor parameters to initialize objects. Since some of the Strings are evaluated lazily, I need to store them as GString.
class SomeClass {
GString foo
}
new SomeClass(
foo: "Hello, world" // fails
)
So method-overloading is no option.
The solution is as mentioned by willyjoker to use CharSequence instead of String
class SomeClass {
CharSequence foo
}
new SomeClass(
foo: "Hello, world" // works
)
new SomeClass(
foo: "Hello, ${baa}" // also works lazily
)

There is probably no good reason to have a method accepting only GString as input or output. GString is meant to be used interchangeably as a regular String, but with embedded values which are evaluated lazily.
Consider redefining the method as:
void foo (String baa)
void foo (CharSequence baa) //more flexible
This way the method accepts both String and GString (GString parameter is automagically converted to String as needed). The second version even accepts StringBuffer/Builder/etc.
If you absolutely need to keep the GString signature (because it's a third party API, etc.), consider creating a wrapper method which accepts String and does the conversion internally. Something like this:
void fooWrapper (String baa) {
foo(baa + "${''}")
}

You can create overloaded methods or you can use generics; something as below:
foo("Hello from foo GString ${X}")
foo("Hello from foo")
MyClass.foo("Hello from foo GString ${X}")
MyClass.foo("Hello from foo")
// method with GString as parameter
void foo(GString g) {
println("GString: " + g)
}
// overloading method with String as parameter
void foo(String s) {
println("String: " + s)
}
// using generics
class MyClass<T> {
static void foo(T t) {
println(t.class.name + ": " + t)
}
}

Related

How to refer to implementor class with #ClosureParams from trait

I would like to use #ClosureParams with a method in a trait, that takes a Closure as input, which will be passed the trait's implementer when called.
Consider the following example:
trait Fooable {
void foo(#ClosureParams(????) Closure callable) {
callable.call(this)
}
}
class Bar implements Fooable {
String baz
}
new Bar().foo { it.baz == "foo'ed" }
How do I tell the static analyser that the it passed to the closure is actually Bar (last line). What should value should I pass to #ClosureParams in the definition of the foo method?
It won't work with traits at the moment (Groovy 2.4.13), because there is no ClosureSignatureHint implementation that allows you to define at a runtime type of a hint that uses class type that implements method from trait interface. If your trait was implemented only by Bar class then you could specify closure parameter type as:
#CompileStatic
#TypeChecked
trait Fooable {
void foo(#ClosureParams(value = SimpleType, options = ["Bar"]) Closure callable) {
callable.call(this)
}
}
But it's not the case.
#ClosureParams won't even recognize generic type if used with trait. Let's consider following definition:
#CompileStatic
#TypeChecked
trait Fooable<T> {
void foo(#ClosureParams(value = SimpleType, options = ["T"]) Closure callable) {
callable.call(this)
}
}
We could expect that Bar class that implements Fooable<Bar> should work like a charm, but it does not unfortunately:
Closure parameter in this case is recognized as T type. It happens because method foo is implemented inside Bar class and #ClosureParams(value = SimpleType.class,options = {"T"}) is also compiled at Bar class level, so it is not aware of generic type T. Let's take a look at compiled Bar class to understand what's going on:
public class Bar implements Fooable<Bar>, GroovyObject {
private String baz;
public Bar() {
String var1 = "test";
this.baz = var1;
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
Helper.$init$(this);
Object var10000 = null;
}
#TraitBridge(
traitClass = Fooable.class,
desc = "(Lgroovy/lang/Closure;)V"
)
public void foo(#ClosureParams(value = SimpleType.class,options = {"T"}) Closure arg1) {
Helper.foo(this, arg1);
Object var10000 = null;
}
// some other methods
}
This is what you will see if you open Bar.class as a decompiled file.
Generics will work fine if instead of trait we would use abstract class. In this case abstract generic class Fooable<T> would implement foo method so Bar class would refer to implementation from Fooable<T> class - a class that is aware of T type. In this case IDE would resolve T correctly and suggest Bar instead.
So what are the options when using trait in this case? You could try implementing your own ClosureSignatureHint class, but this is not that easy. I did a small experiment - I have defined NewSimpleType class and I have copied 1:1 sources from SimpleType class. Then I used it as:
#CompileStatic
#TypeChecked
trait Fooable {
void foo(#ClosureParams(value = NewSimpleType, options = ["Bar"]) Closure callable) {
callable.call(this)
}
}
As you can see I've only replaced Groovy's SimpleType with my custom NewSimpleType. It didn't work. My IDE (IntelliJ IDEA Ultimate 2017.3.3) didn't resolve any type. I've even move this class to a separate Maven project and I've build it and added as a dependency - didn't work as well.
I assume it should be possible to implement a hint class that takes caller class type into account. There are some implementations that take closure parameter type from first, second or third parameter. It sounds doable, at least in theory.
Last option that requires least effort is just provide closure parameter type explicitly, e.g.
Bar bar = new Bar()
bar.foo { Bar b -> b.baz }
It supports all code completion features. The downside is that you can specify different type, like:
Bar bar = new Bar()
bar.foo { String b -> b.toLowerCase() }
IDE won't complain about that, but it will fail while compiling.
Custom StringParameterHint use case
I have created for experiments a static closure signature hint that accepts only java.lang.String as a parameter:
public class StringParameterHint extends ClosureSignatureHint {
#Override
public List<ClassNode[]> getClosureSignatures(MethodNode node, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options, ASTNode usage) {
final List<ClassNode[]> list = new ArrayList<>();
list.add(GenericsUtils.parseClassNodesFromString("java.lang.String", sourceUnit, compilationUnit, node, usage));
return list;
}
}
Then I've set it up with #ClosureParams in Fooable.foo(Closure cl) method. Unfortunately IDE does not read this hint and does not recognize it as a type of String:
But compiler (in IDE) is aware of this closure parameter hint and if I cast parameter to Bar like:
bar.foo { Bar b -> b.baz }
then IDE does not mark it as an incorrect expression, yet compilation fails and program does not start:
Error:(11, 19) Groovyc: Expected parameter of type java.lang.String but got tld.company.Bar
Error:(11, 28) Groovyc: [Static type checking] - No such property: baz for class: java.lang.String
So it looks like we can force compiler to be closure parameter aware, but this information is not being read by IDE (IntelliJ IDEA 2017.3.3 in my case). I guess this might be an IDE issue. I've even moved this StringParameterHint class to groovy.transform.stc package (I was assuming that maybe IDE loads all hints from this package automatically), but it didn't help.

In Haxe, how do you pass Enum values in functions, and then convert them to Strings within the function?

I can't seem to get this working, but I'd be surprised if it wasn't possible in Haxe.
I'm trying to pass a couple of Enum values defined in my game to a function, so that it can then concatenate them as String types and pass that to other functions.
Example:
// In a general Entity class:
public override function kill():Void {
messages.dispatchCombined(entityType, ListMessages.KILLED);
super.kill();
}
And in my Messages.hx class:
package common;
import msignal.Signal.Signal1;
/**
* A Message / Event class using Signals bound to String names.
* #author Pierre Chamberlain
*/
class Messages{
var _messages:MessagesDef;
public function new() {
_messages = new MessagesDef();
}
public function add(pType:String, pCallback:FuncDef) {
if (_messages[pType] == null) {
_messages[pType] = new Signal1<Dynamic>();
}
var signals = _messages[pType];
signals.add( pCallback );
}
public function dispatch(pType:String, pArg:Dynamic):Bool {
var signals = _messages[pType];
if (signals == null) return false;
signals.dispatch(pArg);
return true;
}
//Compiler doesn't like passing enums :(
public inline function addCombined(pSource:Enum, pEvent:Enum, pCallback:FuncDef) {
add( combine(pSource, pEvent), pCallback );
}
public inline function dispatchCombined(pSource:Enum, pEvent:Enum, pArg:Dynamic):Bool {
return dispatch( combine(pSource, pEvent), pArg);
}
//How can I just pass the enum "names" as strings?
static inline function combine(a:Enum, b:Enum):String {
return String(a) + ":" + String(b);
}
}
typedef MessagesDef = Map<String, Signal1<Dynamic>>;
typedef FuncDef = Dynamic->Void;
Note how addCombined, dispatchCombined and combine expect an "Enum" type, but in this case I'm not sure if Haxe actually expects the entire Enum "class" to be passed (ie: ListMessages instead of ListMessages.KILLED) or if a value should work. Anyways, compiler doesn't like it - so I'm assuming another special Type has to be used.
Is there another way to go about passing enums and resolving them to strings?
I think you need EnumValue as parameter type (if it is only for enum values), and use Std.String to convert to String values.
static inline function combine(a:EnumValue, b:EnumValue):String {
return Std.string(a) + ":" + Std.string(b);
}
Of course that can be written smaller using String interpolation:
static inline function combine(a:EnumValue, b:EnumValue):String {
return '$a:$b';
}
Of course that can be 'more dynamic' using type parameters:
static inline function combine<A, B>(a:A, b:B):String {
return '$a:$b';
}
There is totally no need to use Dynamic as suggested. If you use Dynamic, you basically turn off the type system.
live example:
http://try.haxe.org/#a8844
Use Dynamic instead of Enum or pass them as Strings right away since you can always convert to enum from String if you need it later.
Anyway pass the enum as enum:Dynamic and then call Std.string(enum);
EDIT: Using EnumValue is definitely better approach than Dynamic, I use Dynamic in these functions because I send more than just Enums there and I am not worried about type safety in that case.

Why can constructor with single parameter be invoked without any arguments at all?

class Foo {
public Foo(String s) {}
}
print new Foo()
Why does this code work?
If I declare constructor with parameter of primitive type the script fails.
Groovy will do its best to do what you asked it to do. When you call new Foo(), it matches the call to calling new Foo( null ) as there is a constructor that can take a null value.
If you make the constructor take a primitive type, then this cannot be null, so Groovy throws a Could not find matching constructor for: Foo() exception as you have seen.
It does the same with methods, so this:
class Test {
String name
Test( String s ) {
this.name = s ?: 'tim'
}
void a( String prefix ) {
prefix = prefix ?: 'Hello'
println "$prefix $name"
}
}
new Test().a()
prints Hello tim (as both constructor and method are called with a null parameter)
wheras:
new Test( 'Max' ).a( 'Hola' )
prints Hola Max
Clarification
I asked on the Groovy User mailing list, and got the following response:
This is valid for any method call (not only constructors) and I (as well as others) really dislike this "feature" (because it's very error prone) so it will probably disappear in Groovy 3. Also, it's not supported by static compilation :)
So there, it works (for now), but don't rely on it :-)

How do I convert a Groovy class constructor into a Closure?

So Groovy has this relatively handy syntax to convert methods into closures, e.g.
[1,2,3].each { println it }
// is equivalent to
[1,2,3].each this.&println
But how do I convert a class Constructor, e.g
[1,2,3].collect { new Thing( it ) }
// is equivalent to
[1,2,3].collect ????
Groovy's reflection has Thing.constructors List to inspect, but I can't figure out where to put the ampersand in Thing.constructors[0].
You can use invokeConstructor metaClass method that invokes a constructor for the given arguments.
class Thing {
Thing(Integer num) { this.num = num }
Integer num
}
[1,2,3].collect Thing.metaClass.&invokeConstructor

Groovy AST Transformations - How can I figure out the return type of a MethodCallExpression?

With Groovy AST Transformations, how can I figure out the return type of a MethodCallExpression?
MethodCallExpression.getType() always returns java.lang.Object even if I explicitly define the return type of the method in the method definition.
Due to the dynamic nature of groovy, the AST can't know the return type of a method call expression at compile time. For example:
class Example {
String foo() { "foo" }
}
def e = new Example()
assert e.foo() == "foo"
Looks simple enough. foo returns a string, so the MethodCallExpression for e.foo() should have a type of String, right? But what if foo is changed in the metaClass?
class Example {
String foo() { "foo" }
}
def e = new Example()
if (someRuntimeCondition) {
e.metaClass.foo = { -> 42 }
}
assert e.foo() == "foo" // is foo a String or an Int?
The groovy compiler just doesn't have enough information to make any assumptions about the method call since it could change at runtime, so it has to compile it down to an Object.

Resources