A short example of why I prefer static typing: learning Gradle

I make no secret of the fact the I prefer to program in statically-typed languages. That is not to say that I don’t write programs in dynamically typed languages. In fact, I have written and will continue to write programs in Perl, Python, Ruby, JavaScript, and other dynamically typed programs, because pragmatically speaking, there is a whole lot more to programming to get something done than questions of type systems.

Nevertheless, it is always painful to me when I hit a wall when learning and using an unfamiliar API from a dynamically typed language. I always think, “if only this API were statically typed, so that when I encounter an error, I can immediately look up what went wrong”.

Here’s an example as I’ve been learning Gradle while evaluating it along with learning SBT, as two candidate build tools for me to switch to out of Maven as my build tool for a primarily Java-based project. Note that Gradle is basically an embedded domain-specific language using the dynamically typed language Groovy, while SBT is an embedded domain-specific language using the statically typed language Scala.

Code example

Here’s a code example from a book I’ve been going through while studying Gradle, “Building and Testing with Gradle”:

Example 3.21. Use the Ant path references inside the Gradle build

ant.importBuild 'build.xml'

defaultTasks = ['gradleBuild']

////////////////////////////////////////////////////////////////////////
// Gradle paths retrieved from Ant and then built using a Gradle task
repositories {
    flatDir name: 'localRepository1', dirs: ant.references['antPathToLibs1']
    flatDir name: 'localRepository2', dirs: ant.references.antPathToLibs2
    flatDir name: 'localRepository3', dirs: "antlibs"
}

task gradleBuild << {
    //Set classpath to include the JAR
    println "The repositories bridged from Ant to Gradle are:"
    println repositories*.name

    println repositories.localRepository1

    println repositories['localRepository2']

    println "localRepository3 class = " + repositories.localRepository3.class
    println "localRepository3 name = " + repositories.localRepository3.name
    println "localRepository3 latest = " + repositories.localRepository3.latest
    println "localRepository3 ivyPatterns = " + repositories.localRepository3.ivyPatterns
    println "localRepository3 artifactPatterns =
       " + repositories.localRepository3.artifactPatterns
    println "localRepository3 checkconsistency =
       " + repositories.localRepository3.checkconsistency
    println "localRepository3 m2compatible =
       " + repositories.localRepository3.m2compatible
    println "localRepository3 methods =
       " + repositories.localRepository3.metaClass.methods*.name.sort().unique()
}

Error output

Unfortunately, this Gradle build file, when executed under the current version of Gradle, dies with an error:

$ ./run-example.bsh
:gradleBuild
The repositories bridged from Ant to Gradle are:
[localRepository1, localRepository2, localRepository3]
org.gradle.api.internal.artifacts.repositories.DefaultFlatDirArtifactRepository_Decorated@7f205d8d
org.gradle.api.internal.artifacts.repositories.DefaultFlatDirArtifactRepository_Decorated@5eef2e7c
localRepository3 class = class org.gradle.api.internal.artifacts.repositories.DefaultFlatDirArtifactRepository_Decorated
localRepository3 name = localRepository3
:gradleBuild FAILED

FAILURE: Build failed with an exception.

* Where:
Build file 'oreilly-gradle-book-examples/ant-classpathanttogradle/build.gradle' line: 24

* What went wrong:
Execution failed for task ':gradleBuild'.
> Could not find property 'latest' on org.gradle.api.internal.artifacts.repositories.DefaultFlatDirArtifactRepository_Decorated@8c1e4d5.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

After much searching online, I still could not figure out what properties are supposed to be available from repositories.localRepository3. I looked at the Gradle documentation, and I looked up the package org.gradle.api.internal.artifacts.repositories without success.

Gradle’s API has changed since the publication of the book, obviously. And in fact, I have been submitting GitHub pull requests as I update the code examples while encountering apparent changes. But sometimes, as in this case, it has been too confusing for me to figure out how to update the code. You could say that this is always the case for any code in any language, but what bothers me is the nature of error messages and documentation in APIs for dynamically typed languages such as Groovy. These experiences remind me that types are documentation, that types are a way for a programmer to tell the world how code is supposed to be used.

Conclusion

Gradle seems to be a promising build tool, and I will continue to evaluate it while skipping over the particular mysterious error I encountered (since it is not really relevant to what I want to do in my builds), but I wish I could have better figured out what went wrong with the book’s code example. Meanwhile, it remains to be seen what kinds of problems I will encounter when using SBT. Again, I have to emphasize that my goal is to get things done, correctly and efficiently. Whatever works best, I will use, whatever its drawbacks: in another project, I happily used SCons as a build tool, based on the dynamically typed language Python, and of course, I have used Rake for Ruby projects.

(Update of 2015-05-18)

Wow, that was written in fall of 2012.

Since then, I have heavily used SBT from then to the present first for Java, then for Scala, which became my main programming language in early 2013.

Also, in early 2015, Pivotal got out of the Groovy business.

Meanwhile, in early 2015 as I adopted Haskell as my main language for new projects, I switched away from SCons as a general-purpose build tool to Haskell-based Shake, which works really great for me.

Comments (2)Archived from Disqus

JoelMcCrackenView on Disqus ↗

How do you feel about static typing at edges and dynamic typing in internals, ala http://docs.racket-lang.org...

Personally, I prefer dynamic typing. However, I think that having the edges be checked provides most of the benefit that you're referring to.

Franklin ChenView on Disqus ↗

I haven't used Racket, but looking at that documentation about contracts, I gather that they are really a dynamic runtime add-on, and are not part of a static typing discipline. I think the concept of contract is useful, since there will always be interesting properties one may want to check that cannot be encoded in a practical static type system, but you should also realize that ongoing research has been investigating dependent types as a way of tackling some of the issues addressed by contracts.