The Artima Developer Community
Sponsored Link

Scala Buzz
A Cross-Language Generics Trick - Java, Scala and C#

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Ricky Clarkson

Posts: 63
Nickname: rclarkson
Registered: Jul, 2006

Ricky Clarkson is a salsa-dancing, DJing programmer in Manchester, England.
A Cross-Language Generics Trick - Java, Scala and C# Posted: Jun 24, 2008 6:30 PM
Reply to this message Reply

This post originated from an RSS feed registered with Scala Buzz by Ricky Clarkson.
Original Post: A Cross-Language Generics Trick - Java, Scala and C#
Feed Title: Ricky's technical blog
Feed URL: http://rickyclarkson.blogspot.com/feeds/posts/default
Feed Description: A blog about programming languages, and Java.
Latest Scala Buzz Posts
Latest Scala Buzz Posts by Ricky Clarkson
Latest Posts From Ricky's technical blog

Advertisement
Given a Pair<T, U> type in Java, Scala or C#, such as Map.Entry, Tuple2 or KeyValuePair respectively, you can construct type-checked variadic heterogenous containers that you can write general methods to operate on.

Let's write a Pair interface for Java and C#:

interface Pair<T, U> {
 T _1();
 T _2(); }
For Scala we'll use Tuple2, which has _1 and _2 as well. You could use Map.Entry and KeyValuePair in Java and C# respectively, but they seem to have extra semantic information in their names. I know some readers will be crying out for more semantic information than _1 and _2, but I hope they bear with me a moment.

Eliding the implementation, one could have a line of code like the following easily:

Java: Pair<String, Integer> pair = Pairs.pair("hello", 5);
Scala: val pair=("hello", 5)
C#: val pair = Pairs.Pair("hello", 5);
I expect that's fine with most people. For C# you'd probably change 'pair' to 'Pair' for the method name. Then, to start introducing the trick:
Java: Pair<Double, Pair<String, Integer>> withDouble = Pairs.pair(3.0, pair);
Scala: val withDouble = (3.0, pair)
C#: var withDouble = Pairs.Pair(3.0, pair);
You can see that the type in the Java code starts to look a little messy; this is no accident. Explicit static typing makes us more likely to choose less expressive types. Anyway, we can add a method 'prepend' to the Pair type, which doesn't modify anything, but returns a new Pair consisting of a data item on the left and the original Pair on the right. So we get:
Java: Pair<Double, Pair<String, Integer>> pair = Pairs.pair("hello", 5).prepend(3.0);
Scala: val pair = Pairs.pair("hello", 5) prepend 3.0
C#: var pair = Pairs.Pair("hello", 5).Prepend(3.0);
So prepend must be an interesting method, because it looks like you can use it to add more type parameters to something. Clearly you can't, I'm just chaining Pairs, but it makes a nice effect. So far not very useful; I'll get to that. First let's implement prepend:
Java:
 public class Pair<T, U> { ...
  public <V> Pair<V, Pair<T, U>> prepend(V v) { return pair(v, this); } }
Scala:
 implicit def Tuple2WithPrepend[T, U](tuple: (T, U)) = new {
  def prepend[V](v: V) = (v, tuple) }
C#:
 public class Pair<T, U> { ...
  public Pair<V, Pair<T, U>> Prepend<V>(V v) { return pair(v, this); } }
The nice part about this way of building up Pairs is that you can write methods to handle them instead of writing one per Pair arity. Specifically, you could gather up parameters for an immutable class then instantiate it in one go. In fact, that's what I do in a prototype for a JDBC wrapper. To wet the tastebuds (sorry, only Java for this one):
List<QuestionInfo> questions=select(conn).asString("question").asString("correct").asString("wrong").as(question).from("questions").toList();
The idea is that the above runs the SQL query: select question, correct, wrong from questions and constructs a QuestionInfo for each result, putting the result into a list.

The surprising thing is probably that there's no reflection or casting going on at all. Each asString (well, after the first one really) builds up more in a chain of generic types, then the .as(question) deconstructs them again. question is actually an F<Pair<String, Pair<String, String>>, QuestionInfo>, which means it's a function that takes 3 Strings and returns a QuestionInfo, roughly.

The above code comes from a working test case I published here.

It turns out that someone else had this idea way way way before I did, and made something professional out of it, though only some of that appears to be statically type-checked.

I hope that what I've showed here proves useful to you, and if you are my team leader and I pointed you at this page, remember that you saw it on the Internet, it's real, so you have to let me write it in our project.

Read: A Cross-Language Generics Trick - Java, Scala and C#

Topic: Programming in a natural order Previous Topic   Next Topic Topic: Lambda in the Sun

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use