On 07/03/2013, at 6:26 AM, Luke Daley wrote:

> Resurrecting this as I have a story for it.
> 
> On 04/02/2013, at 5:04 PM, Luke Daley <[email protected]> wrote:
> 
>> Hi,
>> 
>> I'm working on a spec for implicit file value coercion. This will likely 
>> yield some more general stuff for implicit type coercion, but the focus is 
>> for file values. There are a few tricky problems.
>> 
>> In order to support “flexible input values” for files right now, you need to 
>> do effectively this…
>> 
>> class MyTask extends DefaultTask {
>> 
>>      Object someFile
>> 
>>      File getSomeFile() {
>>              someFile == null : null : project.file(someFile)
>>      }
>> }
>> 
>> This allows (among other values) (1) …
>> 
>> task myTask(type: MyTask) {
>> someFile "path/to/file"
>> }
>> 
>> And (2):
>> 
>> task myTask(type: MyTask) {
>> someFile { someLazilyEvaluatedValue }
>> }
>> 
>> We want to avoid developers having to do this manually for a few reasons. 
>> Developers should be able to just write the following and get the same 
>> behaviour:
>> 
>> class MyTask extends DefaultTask {
>>      File someFile
>> }
>> 
>> If we ignore #2 for the time being, we could do this by injecting a coercion 
>> layer in the MOP. We would effectively intercept *every* method call, if 
>> it's a coercible case then coerce the value and just call through to the 
>> declared setter. That's actually quite a big change as it flips the 
>> relationship around between the Groovy MOP and ours, but it's ultimately 
>> doable I think.
> 
> I was wrong here. We already route every method call through our MOP (of 
> course) so this isn't a big deal.
> 
>> #2 provides lazy evaluation because the closure gets stored and evaluated on 
>> demand by project.file() in the getter. If we decide that this story 
>> (implicit file value coercion) means supporting this usage, then we are 
>> forced to answer some uncomfortable questions about laziness and what we 
>> call convention mapping. 
>> 
>> Options:
>> 
>> 1. Do not support deferring with this feature, all values are coerced 
>> immediately.
>> 2. Do support deferring, ultimately meaning on-demand coercion
>> 
>> I'm not sure how much of an option #1 actually is, given how widespread the 
>> use of this feature is.
>> 
>> If we go with #2, what do we do about the crossover with convention mapping? 
>> How much of that needs to be considered for this story? if we completely 
>> ignore it and add another layer, we'd now have two different out-of-object 
>> places that object data is stored. It starts to look like we should look at 
>> some convergence here. If so, that raises the question of whether we can 
>> change any convention mapping API without a deprecation cycle (if it turns 
>> out we need to). 
> 
> I think for now we can ignore #2 until we have a clearer answer on where we 
> are going with this. 

I want the coercion to be eager and for us to offer better ways to defer 
configuration, at a coarser domain-object level rather than at the property 
level. So:

myTask.someFile  = someValue

is the same as

myTask.setSomeFile(myTask.project.file(someValue)).

The coercions will be available via a set of notation parsers (or whatever the 
notation parsers turn into), and the convention mapping stuff will use the 
parsers as well. So:

myTask.conventionMapping.someFile = { someValue }

will apply the conversion of someValue to File at the time that it needs to 
provide the value.


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com
Join us at the Gradle Summit 2013, June 13th and 14th in Santa Clara, CA: 
http://www.gradlesummit.com

Reply via email to