inPracticeScala
!"#$%"&'()'$"#'*© ThoughtWorks, Inc 2010
!"#$%"&'#()*+,#-*$.&/-%+"0.1*+#(%2-/*2&/#-*+$-30(*&2-3+2-3+1.2-+"#4+&#+$*0+&"0+.2&0*&+/--#52&/#-*+/-+6!+2-3+*#7&42(0+&08"-#.#%9+&#+3/770(0-&/2&0+1(#3$8&*:+*&02.+;2()0&+*"2(0:+3(2;2&/82..9+8$&+&"0+&/;0+2-3+0<10-*0+#7+8(02&/-%+*#7&42(0+1(#3$8&*:+#(+2..=#7=&"0=2>#50+2-3+;#(0?+
@#(0+&"2-+)001/-%+$1
Optimising ITOrganisations
technology
Consulting
Testing
Build & ReleaseManagement
Experience Design
Our consulting engagements, like our delivery projects, are collaborative and fast. The focus is on creating a shared understanding, solving problems and making practical improvements, not just leaving you with slideware and reports. Each and every one of our consultants is active in delivery projects, not purely focused on advisory work. We are well known and respected in academia, but our thinking is firmly grounded in reality. We’re business savvy – and know that quick wins are just as important as long-term goals.
Be warned. We aren’t going to concentrate on an easy way out – our consultants will always give you the honest, full picture – even if it means pointing out something large and difficult. The root cause is important if we are actually going to fix anything, so we won’t shy away from it. But once we are agreed on the causes, the road map we create for improvement will deliver measurable value at regular, well-paced, milestones. No matter what the problem or objective, we don’t believe in plans with all results skewed to the end. And neither should you.
Offices
2
presentation Containscode examples
Warning
ContextA bit of
about the project
?Why Scala
What is Scala
?
Scala
FunctionalObjectOriented
StaticallyTyped
Scriptable
JVM-based
JVMByte CodeCompileScala
Benefits
“Functional
Programming”
List<String> features = Arrays.asList("noFeature", "featureC", "featureB", "featureA");
for (String feature : features) { System.out.println(feature);}
List<String> javaFeatures = Arrays.asList("noFeature", "featureC", "featureB", "featureA");
for (String feature : javaFeatures) { System.out.println(feature);}
val scalaFeatures = List( "noFeature", "featureC", "featureB", "featureA")
scalaFeatures foreach { println _ }
val currentFeatures = List("noFeature", "featureC", "featureB", "featureA")
Result: "featureA_toggle, featureB_toggle"
currentFeatures.filter { _.startsWith("feature") }
.mkString(", ") .map { _ + "_toggle" }
.sorted .take(2)
?
String result = "";List<String> currentFeatures = Arrays.asList("noFeature", "featureC", "featureB", "featureA");Collections.sort(currentFeatures);
int i = 0, j = 0, take = 2; while(i < take && j < currentFeatures.size()) { String feature = currentFeatures.get(j++);
if(feature.startsWith("feature")) { result += feature + "_toggle" + ((i++ != take) ? ", " : ""); }}
currentFeatures.filter { _.startsWith("feature") }
.mkString(", ") .map { _ + "_toggle" }
.sorted .take(2)
“Less
Boilerplate”
public class Property {}
class Property {}
public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; } }
class Property(key: String, value: String, default: String) {}
public class Property { // ... public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; }
}
class Property(val key: String, val value: String, val default: String) {}
public class Property { // ... public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); }
@Override public String toString() { return String.format("%s=%s", key, value); }
}
class Property(val key: String, val value: String, val default: String) {
def changeTo(newValue: String) = new Property(key, newValue, default)
def reset = new Property(key, default)
override def toString = "%s=%s" format(key, value)}
public class Property { // ...
@Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; }}
case class Property(key: String, value: String, default: String) {
def changeTo(newValue: String) = Property(key, newValue, default)
def reset = Property(key, default)
override def toString = "%s=%s" format(key, value)}
public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; }
public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; } public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); }
@Override public String toString() { return String.format("%s=%s", key, value); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; }}
case class Property(key: String, value: String, default: String) { def changeTo(newValue: String) = Property(key, newValue, default) def reset = Property(key, default) override def toString = "%s=%s" format(key, value)}
public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; }
public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; } public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; }
@Override public String toString() { return String.format("%s=%s", key, value); }}
case class Property(key: String, value: String, default: String) { def changeTo(newValue: String) = Property(key, newValue, default) def reset = Property(key, default) override def toString = "%s=%s" format(key, value)}
45 vs 5LOCs
* ignoring blank lines
Another Example:{ Blocks }
“Less
Boilerplate”
val content = get("http://foo.com") { toString }
val statusCode = get("http://foo.com") { statusCode }
get("http://foo.com")
int statusCode = httpSupport.get("http://foo.com", new Block<Integer>() { public Integer execute(HttpResponse response) { return response.getStatusLine().getStatusCode()); }});
String content = httpSupport.get("http://foo.com", new Block<String>() { public Integer execute(HttpResponse response) { return EntityUtils.toString(response.getEntity()); }});
httpSupport.get("http://foo.com", new VoidBlock() { public void execute(HttpResponse response) {}});
“Java
Compatibility”
java -cp "lib/*" com.springer.core.app.Casper
Scala library goes in here!
val java = new java.util.ArrayList[String]
val scala: Seq[String] = foo filter { ... }
val log = Logger.getLogger("com.foo")
log.debug("Hello!")
“Gentle
Learning Curve”
“Java without ;”
Libraryre-use JVM
<XML-Support/>
<PdfInfo hasAccess="false"> <DDSId>12345</DDSId> <ContentType>Article</ContentType> </PdfInfo>
val pdfInfo =
val contentType = (pdfInfo \ "ContentType").text
val hasAccess = (pdfInfo \ "@hasAccess").text.toBoolean
val pdfInfoList = <PdfInfoList> <PdfInfo hasAccess="false"> <DDSId>12345</DDSId> <ContentType>Article</ContentType> </PdfInfo> <PdfInfo hasAccess="true"> <DDSId>54321</DDSId> <ContentType>Book</ContentType> </PdfInfo> </PdfInfoList>
case class PdfInfo(xml: NodeSeq) { val ddsId = (xml \ "DDSId").head.text val hasAccess = (xml \ "@hasAccess").head.text val contentType = (xml \ "ContentType").head.text}
(pdfInfoList \ "PdfInfo") map { PdfInfo(_) }
“DSL-
Friendly”
System
App2
App3
Functional
App1
Unit
DB
Integration
Levels of Testing
Unit Test DSL
class PropertyTest extends Spec with ShouldMatchers {
it("should render key and value separated with an equals sign") { Property("shark", "fish").toString should equal ("shark=fish") } }
class ForgottenPasswordPageTests extends FunctionalTestSpec with ForgottenPasswordSteps with Uris {
it("should validate user email") { given(iNavigateTo(ForgottenPasswordPage)) when(iSubmitEmail("[email protected]")) then(iShouldSeeInvalidEmailErrorMessage) }
}
Functional Test DSL
Document Validation DSL
trait DDSValidationRules extends JournalXPaths with APlusPlusElements { val journalRules = List( No(JournalTitle) is Invalid, MoreThanOneElementIn(JournalArticle) is Invalid, BadLanguageIn(JournalArticleLanguage) is Invalid,
JournalIssue With No(JournalIssueCoverYear) is Invalid, Bibliography With No(BibliographyHeading) is Invalid,
MarkupIn(JournalTitle) is Unsupported, MarkupIn(JournalAuthor) is Unsupported )
}
“Active
Community”
“Fun”
“Fun”
“Fun”
val foo = (7 to 21)
“Fun”
List<Integers> foo = new ArrayList<Integers>()for (int i = 7; i <= 21; i++) { foo.add(i); }
“Fun”
def eat(food: String = "plankton")
“Fun”
public void eat(String food) {...}
public void eat() { eat("plankton");}
httpSupport.get("http://foo.com", new Block<Integer>() { public Integer execute(HttpResponse response) { return response.getStatusLine().getStatusCode()); }});
“Fun”
Drawbacks
“Tool
Support”
!?
1:10
“More rope...”
def ++ [B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]):That
Scala Collection Library
class JournalPageTests extends Spec with FunctionalTestSpec with JournalPageSteps with SearchResultsPageSteps with LanguageSteps with LanguageSupport with JavascriptSupport with Uris with IssuePageSteps with VolumesAndIssuesPageSteps with CoverImageUrlBuilders with FakeEntitlementSteps with FullTextPageSteps with CommonAbstractSteps with GoogleAnalyticsSteps with ActionSectionSteps { ... }
Traits Galore in our codebase...
xsbt
scalaz
→Can Should
Risk Mitigations
Would wedo it again
?
disclaimer
use scalaat your own risk
Thanks for
Listening
@patforna | @mhoefi