1

Lets say I have a class:

Foo {
 always()
 onlyScopeB()
}

And I have different methods, which take different closures: scopeA, scopeB

foo = new Foo()

scopeA{
  foo.always()     // this should COMPILE
  foo.onlyScopeB() // this should NOT COMPILE
}

scopeB{
  foo.always()     // this should COMPILE
  foo.onlyScopeB() // this should COMPILE
}

Is there anyway to achieve this at the compilation stage? I am writing a DSL and I have scopes that correspond to stages in a process and sometimes fields are null in one scope, and then other times they are not-null and I am trying to provide the best semantic experience to find errors easily.

Derrops
  • 7,651
  • 5
  • 30
  • 60
  • If the syntax change of my answer below does not work for you, you can use Static Type Checking extension to examine each method call and based on the enclosing closure, choose to emit an error or continue. https://docs.groovy-lang.org/latest/html/documentation/#_type_checking_extensions – emilles Sep 24 '22 at 13:50

2 Answers2

1

This is slight variation on your stated syntax. You can divide your Scope A and Scope B methods into interfaces and use closure delegation to provide feedback. The common method(s) like always() could be moved to a common interface if there are many. If you enable Static Type Checking on the script part of this, you will get compiler errors instead of just underlines.

interface Bar {
  void always()
  void onlyScopeA()
}
interface Baz {
  void always()
  void onlyScopeB()
}
@groovy.transform.AutoImplement
class Foo implements Bar, Baz {
}

void scopeA(@DelegatesTo.Target Bar bar, @DelegatesTo Closure block) {
  bar.with(block)
}
void scopeB(@DelegatesTo.Target Baz baz, @DelegatesTo Closure block) {
  baz.with(block)
}


def foo = new Foo()
scopeA(foo) {
  always()
  onlyScopeA()
  onlyScopeB()
}
scopeB(foo) {
  always()
  onlyScopeA()
  onlyScopeB()
}

example of IDE feedback

emilles
  • 1,104
  • 7
  • 14
  • This is a good alternative thanks a bunch. This object though `bar/baz` is created outside the scope. In the DSL I am making you can create many instances outside of these scopes, but within the scopes they should resolve to Bar or Baz. – Derrops Sep 25 '22 at 05:45
  • 1
    Should not matter where the object is created. By passing it as a parameter to scopeA and scopeB methods you get a chance to narrow its type. – emilles Sep 25 '22 at 18:34
  • But yes, for this to work you would need to be able to change the type hierarchy of Foo – emilles Sep 25 '22 at 18:37
  • The other option I've been looking at is the `use(SomeClassWIthStaticMethods ){}`. But I don't want users to have to remember to do that syntax. – Derrops Sep 26 '22 at 00:17
0

If you want to achieve this at the compilation stage, the only way I can think of is to write a custom AST Transformation.

Edit: The best source for learning about AST transformations is too look at the ones from Groovy itself: https://github.com/apache/groovy/tree/master/src/main/java/org/codehaus/groovy/transform

Leonard Brünings
  • 12,408
  • 1
  • 46
  • 66
  • Anyway you could write an example ( ; I am aware of the AST was hoping for a vanilla Closure solution though if existed, as writing AST is a big learning curve for me to implement this one feature. – Derrops Sep 25 '22 at 05:47
  • Can you please show me a working gradle project which has an ast? – Derrops Oct 01 '22 at 04:19
  • https://github.com/apache/groovy/tree/master/src/main/java/org/codehaus/groovy/transform and https://github.com/spockframework/spock/ although that is quite an advanced one. – Leonard Brünings Oct 02 '22 at 19:00
  • Haha thanks but I need a minimum example. I've looked at https://blogs.apache.org/groovy/entry/calculating-fibonacci-with-groovy-revisited as an example. Trying to get it working. – Derrops Oct 02 '22 at 22:13
  • I don't really understand, the Groovy docs I linked to contain simple examples. And the Groovy transforms, are real world examples. AST transforms don't require some magic compilation settings, so you can just add them to your existing project. The only thing to note, is that the AST Transform can't be in the same compilation that it should transform, so you need two modules if you want to test it in the same project. – Leonard Brünings Oct 03 '22 at 22:21