How to uses traits as plugins and retain static typing? - groovy

So traits are a very cool feature, but I want to see how I could use them as plugins to a DSL I'm working on. But using traits in this manner would mean adding traits at runtime. The docs clearly demonstrate how to do that using:
def combinedTraits = person.withTraits TraitA, TraitB
Which is great, but it's all done with dynamic typing which makes it hard for IDEs to help with code-completion or to use CompileStatic. The only way I can figure how this might work in a type-safe way would be if Groovy had Type Unions. I'm trying to create a DSL akin to the following:
pipeline( Plugin1, Plugin2 ) {
step("Step1") { Map row ->
accept( row )
}
methodFromPlugin1("....", 123)
methodFromPlugin2("....", 456)
}
Then in the DSL doing the following:
public class PipelineDsl {
void pipeline( List<Class> traits, Closure closure ) {
closure.resolveStrategy =
closure.delegate = this.withTraits( traits )
closure.call()
}
}
But if I wanted to help the IDE out so code completion would work I'd need to add #DelegatesTo annotation on the Closure parameter, and I can't hard code the Traits there. An alternative version might be this:
pipeline {
step("Step1") { Map row ->
}
fromPlugin( Plugin1 ) { Plugin1 p ->
p.methodFromPlugin1("....", 123)
}
fromPlugin( Plugin2 ) { Plugin2 p ->
p.methodFromPlugin2("....", 456)
}
}
A little more clunky because I have to declare the parameter types on the Closures, and I can't easily use annotations provided to simplify it (#DelegatesTo, or #ClosureParam, etc).
Although this model mirrors Gradle's task name(type: PluginTask) {...} definitions for plugins and IDEs generally can follow along with those. But I can't tell what Gradle is doing to help out with that. And if I could simplify the clunky case (say remove the type on the Closure parameter) then I'd be able to implement the more elegant case too.
Any better ideas?

Related

How can I make this Rust code more idiomatic

Recently I started to learn Rust and one of my main struggles is converting years of Object Oriented thinking into procedural code.
I'm trying to parse a XML that have tags that are processed by an specific handler that can deal with the data it gets from the children.
Further more I have some field members that are common between them and I would prefer not to have to write the same fields to all the handlers.
I tried my hand on it and my code came out like this:
use roxmltree::Node; // roxmltree = "0.14.0"
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
let tag_handler: dyn XMLTagHandler = match tag_name {
"name" => NameHandler::new(),
"phone" => PhoneHandler::new(),
_ => DefaultHandler::new()
}
if tag_handler.is_recursive() {
for child in node.children() {
let child_value = get_data_from(&child);
// do something with child value
}
}
let value: String = tag_handler.value()
value
}
// consider that handlers are on my project and can be adapted to my needs, and that XMLTagHandler is the trait that they share in common.
My main issues with this are:
This feels like a Object oriented approach to it;
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field;
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty. e.g.:=> Handler::new(my_other_params, phone_handler_func)
This feels like a Object oriented approach to it
Actually, I don't think so. This code is in clear violation of the Tell-Don't-Ask principle, which falls out from the central idea of object-oriented programming: the encapsulation of data and related behavior into objects. The objects (NameHandler, PhoneHandler, etc.) don't have enough knowledge about what they are to do things on their own, so get_data_from has to query them for information and decide what to do, rather than simply sending a message and letting the object figure out how to deal with it.
So let's start by moving the knowledge about what to do with each kind of tag into the handler itself:
trait XmlTagHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F);
}
impl XmlTagHandler for NameHandler {
fn foreach_child<F: FnMut(&Node)>(&self, _node: &Node, _callback: F) {
// "name" is not a recursive tag, so do nothing
}
}
impl XmlTagHandler for DefaultHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F) {
// all other tags may be recursive
for child in node.children() {
callback(child);
}
}
}
This way you call foreach_child on every kind of Handler, and let the handler itself decide whether the right action is to recurse or not. After all, that's why they have different types -- right?
To get rid of the dyn part, which is unnecessary, let's write a little generic helper function that uses XmlTagHandler to handle one specific kind of tag, and modify get_data_from so it just dispatches to the correct parameterized version of it. (I'll suppose that XmlTagHandler also has a new function so that you can create one generically.)
fn handle_tag<H: XmlTagHandler>(node: &Node) -> String {
let handler = H::new();
handler.foreach_child(node, |child| {
// do something with child value
});
handler.value()
}
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => handle_tag::<NameHandler>(node),
"phone" => handle_tag::<PhoneHandler>(node),
_ => handle_tag::<DefaultHandler>(node),
}
}
If you don't like handle_tag::<SomeHandler>(node), also consider making handle_tag a provided method of XmlTagHandler, so you can instead write SomeHandler::handle(node).
Note that I have not really changed any of the data structures. Your presumption of an XmlTagHandler trait and various Handler implementors is a pretty normal way to organize code. However, in this case, it doesn't offer any real improvement over just writing three separate functions:
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => get_name_from(node),
"phone" => get_phone_from(node),
_ => get_other_from(node),
}
}
In some languages, such as Java, all code has to be part of some class – so you can find yourself writing classes that don't exist for any other reason than to group related things together. In Rust you don't need to do this, so make sure that any added complication such as XmlTagHandler is actually pulling its weight.
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field
Without more information about the fields, it's impossible to really understand what problem you're facing here; however, in general, if there is a family of structs that have some data in common, you may want to make a generic struct instead of a trait. See the answers to How to reuse codes for Binary Search Tree, Red-Black Tree, and AVL Tree? for more suggestions.
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty
Elegance is sometimes a useful thing, but it is subjective. I would recommend closures rather than function pointers, but this suggestion doesn't seem "dirty" to me. Making closures and putting them in data structures is a very normal way to write Rust code. If you can elaborate on what you don't like about it, perhaps someone could point out ways to improve it.

Groovy DSL given syntax validation

Actually I'm experimenting writing a DSL with groovy. So far ...
There are some things unclear to be regarding delegation and intercepting unwanted (Closure) structures:
first of all: How can I throw a (type of?) Exception to point to the correct line of code in the DSL that fails?
assuming
abstract MyScript extends Script {
def type(#DelegateTo(MyType) Closure cl) {
cl.delegate = new MyType()
cl()
this
}
}
under
new GroovyShell(this.class.classLoader, new CompilerConfiguration(scriptBaseClass: MyScript.name)).evaluate(…)
the passed DSL / closure
type {
foo: "bar"
}
passes silently.
I'm aware of, that foo: is just a POJ label but I'm not that sure what that defined Closure is interpreted as?
Neither did I found anything regarding the AST metaprogramming to get in touch of any defined labels to use them?
giving in
type {
foo = "bar"
}
it's clear that he will try to set the property foo, but do I really have to intercept unwanted fields/props by
class MyType {
def propertyMissing(String name) {
… // where I'm unable to println name since this leads to field access 'name' ...
}
}
while user is still allowed to pass
type {
foo "bar"
}
which leads to method not defined .. so I have to write additionally some metaClass.methodMissing or metaClass.invokeMethod stuff ..
meanwhile I tend to dismiss any closures in my dsl only working with simple
def type(Map vars) {
store << new MyType(vars)
// where in the constructor I was forced to write metaClass stuff to validate that only fields are given in the map that are defined in the class
}
that works, but both drafts are not what I expected to do when reading "groovy is so great for making DSLs" ...
I would experiment with the different options and then settle for one.
To guide your users you should give feedback similar to that of the regular compiler (i.e. line-number and column, maybe the expression).
Enforcing the correctness of the input can be non-trivial -- depending on your DSL.
For example:
type {
foo: "bar"
}
Is just a closure that returns the String bar. Is that something your user is supposed to do? The label will be part of the AST, AFAIK in org.codehaus.groovy.ast.stmt.Statement.statementLabels. If you want this syntax to assign something to foo then you'll need to rewrite the AST. The Expression could become a Declaration for the local Variable foo or could become an assignment for the Field foo. That's really up to you, however, Groovy gives you some capabilities that make creating a DSL easier:
You already used #DelegateTo(MyType) so you could just add a Field foo to MyType:
class MyType {
String foo
}
And then either use #CompileStatic or #TypeChecked to verify your script. Note that #CompileStatic will deactivate Run-time Metaprogramming (i.e. propertyMissing etc. won't be called anymore.) while #TypeChecked does not. This, however, will only verify Type-Correctness. That is: assigning to anything but a declared Field will fail and assigning an incompatible Type will fail. It does not verify that something has been assigned to foo at all. If this is required you can verify the contents of the delegate after calling the Closure.

Groovy multiple resource closure

I'm using the resource closure feature of Groovy, and was wondering if it was possible to create one closure that manages two resources. For example, if I have the following two separate closures, is it possible to create one closure that manages both? Or do I really have to nest the closures?
new File(baseDir, 'haiku.txt').withWriter('utf-8') { writer ->
writer.writeLine 'Into the ancient pond'
}
new Scanner(System.in).with { consoleInput ->
println consoleInput.nextLine()
}
No. The syntax method(arg) {} is an alternative syntax to method(arg, {}), thus, you can do this:
fn = { writer ->
writer.writeLine 'Into the ancient pond'
}
new File(baseDir, 'haiku.txt').withWriter('utf-8', fn)
new Scanner(System.in).with(fn)
Note that the closure must contain expected code for both method invocations.

How to reuse code block which describe similiar ant build logic in groovy?

How to reuse code block which describe similiar ant build logic in groovy?
If we have build logic which was implemented by Groovy AntBuilder, just like code below:
ant.someTask(attr1:value1, attr2:value2) {
configuration1(param1:args1, param2:args2){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
configuration2(param3:args3, param4:args4){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
}
Are there any ways to reuse Groovy AntBuilder code block, which could brief the statment in configuration2 ?
I've try to predefine closures and inject them in both configuration,
but it fails with property not found exception while initializing closure.
I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you want a more general purpose solution that allows you to share the whole of the similarStructure block, you need to perform some more advanced work. The trick is to ensure that the delegate of the shared configuration closure is set appropriately:
def sharedConfig = {
similarStructure(additionalArgs:aaa) {
setting1(param5:value5) {
//...blah blah blah
}
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2){
applySharedConfig(delegate, sharedConfig)
}
configuration2(param3:args3, param4:args4){
applySharedConfig(delegate, sharedConfig)
}
}
void applySharedConfig(builder, config) {
def c = config.clone()
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = builder
c.call()
}
Although the applySharedConfig() method seems ugly, it can be used to share multiple configurations across different tasks.
One thing to bear in mind with this solution is that the resolveStrategy of the closure can be very important. I think both DELEGATE_FIRST and OWNER_FIRST (the default) will work fine here. If you run into what appear to be name resolution problems (missing methods or properties) you should try switching the resolution strategy.
I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you are happy to simply share the closure that goes with similarStructure, then the solution is straightforward:
def sharedConfig = {
setting1(param5:value5) {
//...blah blah blah
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
configuration2(param3:args3, param4:args4) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
}
The method that is similarStructure should ensure that the sharedConfig closure is properly configured. I haven't tested this, so I'm not entirely sure. The disadvantage of this approach is that you have to duplicate the similarStructure call with its arguments.

Best groovy closure idiom replacing java inner classes?

As new to groovy...
I'm trying to replace the java idiom for event listeners, filters, etc.
My working code in groovy is the following:
def find() {
ODB odb = ODBFactory.open(files.nodupes); // data nucleus object database
Objects<Prospect> src = odb.getObjects(new QProspect());
src.each { println it };
odb.close();
}
class QProspect extends SimpleNativeQuery {
public boolean match(Prospect p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
}
Now, this is far from what I'm used to in java, where the implementation of the Query interface is done right inside the odb.getObjects() method. If I where to code "java" I'd probably do something like the following, yet it's not working:
Objects<Prospect> src = odb.getObjects( {
boolean match(p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
} as SimpleNativeQuery);
Or better, I'd like it to be like this:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
However, what groovy does it to associate the "match" method with the outer script context and fail me.
I find groovy... groovy anyways so I'll stick to learning more about it. Thanks.
What I should've asked was how do we do the "anonymous" class in groovy. Here's the java idiom:
void defReadAFile() {
File[] files = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.getPath().endsWith(".biz");
}
});
}
Can groovy be as concise with no additional class declaration?
I think it would have helped you to get answers if you'd abstracted the problem so that it didn't rely on the Neodatis DB interface -- that threw me for a loop, as I've never used it. What I've written below about it is based on a very cursory analysis.
For that matter, I've never used Groovy either, though I like what I've seen of it. But seeing as no one else has answered yet, you're stuck with me :-)
I think the problem (or at least part of it) may be that you're expecting too much of the SimpleNativeQuery class from Neodatis. It doesn't look like it even tries to filter the objects before it adds them to the returned collection. I think instead you want to use org.neodatis.odb.impl.core.query.criteria.CriteriaQuery. (Note the "impl" in the package path. This has me a bit nervous, as I don't know for sure if this class is meant to be used by callers. But I don't see any other classes in Neodatis that allow for query criteria to be specified.)
But instead of using CriteriaQuery directly, I think you'd rather wrap it inside of a Groovy class so that you can use it with closures. So, I think a Groovy version of your code with closures might look something like this:
// Create a class that wraps CriteriaQuery and allows you
// to pass closures. This is wordy too, but at least it's
// reusable.
import org.neodatis.odb.impl.core.query.criteria;
class GroovyCriteriaQuery extends CriteriaQuery {
private final c;
QProspect(theClosure) {
// I prefer to check for null here, instead of in match()
if (theClosure == null) {
throw new InvalidArgumentException("theClosure can't be null!");
}
c = theClosure;
}
public boolean match(AbstractObjectInfo aoi){
//!! I'm assuming here that 'aoi' can be used as the actual
//!! object instance (or at least as proxy for it.)
//!! (You may have to extract the actual object from aoi before calling c.)
return c(aoi);
}
}
// Now use the query class in some random code.
Objects<Prospect> src = odb.getObjects(
new GroovyCriteriaQuery(
{ it.url.endsWith(".biz") }
)
)
I hope this helps!
I believe your real question is "Can I use closures instead of anonymous classes when calling Java APIs that do not use closures". And the answer is a definite "yes". This:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
should work. You write "However, what groovy does it to associate the "match" method with the outer script context and fail me". How exactly does it fail? It seems to me like you're having a simple technical problem to get the solution that is both "the groovy way" and exactly what you desire to work.
Yep, thanks y'all, it works.
I also found out why SimpleNativeQuery does not work (per Dan Breslau).
I tried the following and it worked wonderfully. So the idiom does work as expected.
new File("c:\\temp").listFiles({ it.path.endsWith(".html") } as FileFilter);
This next one does not work because of the neodatis interface. The interface does not enforce a match() method! It only mentions it in the documentation yet it's not present in the class file:
public class SimpleNativeQuery extends AbstactQuery{
}
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
In the above, as the SimpleNativeQuery does not have a match() method, it makes it impossible for the groovy compiler to identify which method in the SimpleNativeQuery should the closure be attached to; it then defaults to the outer groovy script.
It's my third day with groovy and I'm loving it.
Both books are great:
- Groovy Recipes (Scott Davis)
- Programming Groovy (Venkat Subramaniam)

Resources