Sunday, March 15, 2009

Disjoint Bounded Views - Redux

I cleaned up my previous post on "stuttering-or". The import would now look like this:


package org.okcjug.imports

class DisjointBoundedView[A,B](val a: Option[A], val b: Option[B])

object DisjointBoundedView {
// namespace pollution? Maybe use "||" or "OrType"
type or[A,B] = DisjointBoundedView[A,B]

// convenience of definition functions
private def da[A,B](a: A): or[A,B] = { new DisjointBoundedView(Some(a),None) }
private def db[A,B](b: B): or[A,B] = { new DisjointBoundedView(None,Some(b)) }
private def na[A,B](n: Nothing): or[A,B] = { new DisjointBoundedView(None, None) }

// implicit defs - stuttering-or
implicit def noneToOr2[A,B](n: Nothing): or[A,B] =
{ na(n) }
implicit def aToOr2[A,B](a: A): or[A,B] =
{ da(a) }
implicit def bToOr2[A,B](b: B): or[A,B] =
{ db(b) }
implicit def aToOr3[A,B,C](a: A): or[or[A,B],C] =
{ da(da(a)) }
implicit def bToOr3[A,B,C](b: B): or[or[A,B],C] =
{ da(db(b)) }
implicit def aToOr4[A,B,C,D](a: A): or[or[or[A,B],C],D] =
{ da(da(da(a))) }
implicit def bToOr4[A,B,C,D](b: B): or[or[or[A,B],C],D] =
{ da(da(db(b))) }
implicit def aToOr5[A,B,C,D,E](a: A): or[or[or[or[A,B],C],D],E] =
{ da(da(da(da(a)))) }
implicit def bToOr5[A,B,C,D,E](b: B): or[or[or[or[A,B],C],D],E] =
{ da(da(da(db(b)))) }
// more? ...

}



Calling code would look like this:

package org.okcjug.test

import org.okcjug.imports.DisjointBoundedView._

class Foo {

def bar[T <% Int or String or Double](t: Option[T]) = {
t match {
case Some(x: Int) => println("processing Int: " + x)
case Some(x: String) => println("processing String: " + x)
case Some(x: Double) => println("processing Double: " + x)
case None => println("empty and I don't care the type")
}
}

def baz[T <% String or Int](t: List[T]) = {
for (x <- t) x match {
case x: String => println("String list item: " + x)
case x: Int => println("Int list item: " + x)
}
}
}

object Foo extends Application {

val f = new Foo

f.bar(None)
f.bar(Some(1))
f.bar(Some("blah"))
f.bar(Some(3.45))
// f.bar(Some(Some(2))) // compiler error
// f.bar(Some(Set("x", "y"))) // compiler error

f.baz(List(1,1,2,3,5,8,13))
f.baz(List("boogie", "woogie"))
// f.baz(List(3.4, 3.14)) // compiler error
// f.baz(List(1,"one")) // compiler error
// f.baz(Some(1)) // compiler error
}



The only difference is that we now don't require clarifying the type for a None, and the class name more accurately reflects what it does.

UPDATE: Per Ittay's suggestion below, I've updated the implementation to use Either as the backing class (shown below). This simplifies the code and allows extraction of values from the composite type via pattern matching. Also, it can no longer accurately be described as "stuttering-or".


package org.okcjug.imports

object DisjointBoundedView {
// namespace pollution? Maybe use "||" or "OrType"
type or[A,B] = Either[A,B]

// implicit defs
implicit def l[T](t: T) = Left(t)
implicit def r[T](t: T) = Right(t)
implicit def ll[T](t: T) = Left(Left(t))
implicit def lr[T](t: T) = Left(Right(t))
implicit def lll[T](t: T) = Left(Left(Left(t)))
implicit def llr[T](t: T) = Left(Left(Right(t)))
implicit def llll[T](t: T) = Left(Left(Left(Left(t))))
implicit def lllr[T](t: T) = Left(Left(Left(Right(t))))
// more? ...

}

Wednesday, March 11, 2009

OKCJUG Slides

Although they may not make much sense without the code and explanation that goes along with them, I've posted the slides used in my JUG presentation yesterday.

Monday, March 9, 2009

Stuttering Or

So I read both Jim McBeath and Michid's blog entries on implementing polymorphism using implicit conversions, especially where it would be otherwise difficult because of type erasure. But, I was uneasy with their approaches. Jim's required implementing redundant classes in a heirarchy and Michid's seemed comlex. Both required that you explicity implement an implicit function for each disjoint type you want one of your method's parameters to be called as. And, even worse, the calling code has to remember to import the implicit functions for this to work.

I call my attempt at a better version the "stuttering-or" method, and it looks like this:

package org.okcjug.typeerasure.arrrgh.util

case class DisjointType[A,B](val a: Option[A], val b: Option[B])

object DisjointType {
type or[A,B] = DisjointType[A,B] // who wants to type "DisjointType" all the time?

// convenience of definition functions
private def da[A,B](a: A): or[A,B] = { DisjointType(Some(a),None) }
private def db[A,B](b: B): or[A,B] = { DisjointType(None,Some(b)) }

// implicit defs - stuttering-or
implicit def aToDisjointType2[A,B](a: A): or[A,B] =
{ da(a) }
implicit def bToDisjointType2[A,B](b: B): or[A,B] =
{ db(b) }
implicit def aToDisjointType3[A,B,C](a: A): or[or[A,B],C] =
{ da(da(a)) }
implicit def bToDisjointType3[A,B,C](b: B): or[or[A,B],C] =
{ da(db(b)) }
implicit def aToDisjointType4[A,B,C,D](a: A): or[or[or[A,B],C],D] =
{ da(da(da(a))) }
implicit def bToDisjointType4[A,B,C,D](b: B): or[or[or[A,B],C],D] =
{ da(da(db(b))) }
implicit def aToDisjointType5[A,B,C,D,E](a: A): or[or[or[or[A,B],C],D],E] =
{ da(da(da(da(a)))) }
implicit def bToDisjointType5[A,B,C,D,E](b: B): or[or[or[or[A,B],C],D],E] =
{ da(da(da(db(b)))) }

}



The "magic" part of this is that it allows infix type notation to represent "Type1 or Type2" as "DisjointType[Type1,Type2]". Then, the implicit defs allow the component types to be represented in a type view. This guards the methods that define it to only accept members of the component types. Note that it is up to the method implementer to cover all the possible cases as the type system will not help you there.

Defining the methods is something like this:


package org.okcjug.typeerasure.arrrgh.work

import org.okcjug.typeerasure.arrrgh.util.DisjointType._

class Foo {

def erasureMethod[T <% Int or String or Double](t: Option[T]) = {
t match {
case Some(x: Int) => println("processing Int: " + x)
case Some(x: String) => println("processing String: " + x)
case Some(x: Double) => println("processing Double: " + x)
case None => println("empty and I don't care the type")
}
}

def erasureMethod2[T <% String or Int](lt: List[T]) = {
for (x <- lt) x match {
case x: String => println("String list item: " + x)
case x: Int => println("Int list item: " + x)
}
}

}



And, the calling code looks like this:

package org.okcjug.typeerasure.arrrgh

import org.okcjug.typeerasure.arrrgh.work.Foo
import org.okcjug.typeerasure.arrrgh.util.DisjointType._

object App extends Application {

val f = new Foo()

f.erasureMethod(Some("blah"))
// f.erasureMethod(None) // compiler error
f.erasureMethod(None.asInstanceOf[Option[String]]) // ok
f.erasureMethod(Some(42))
// f.erasureMethod(Some(List("b"))) compiler error

f.erasureMethod2(List("a","b","c"))
f.erasureMethod2(List(5,4,3,2))
// f.erasureMethod2(List(4.3)) compiler error

}



So, what does this get me? Well, implementing the code is fairly straightforward, just using a "Type1 or Type2 or Type3 ..." construct for the disjoint types (up to 5 in the given code). Also, discerning the specific type within the polymorphic method for purposes of dispatch is done using the standard "match" keyword.

What I don't like about my solution is that the calling code must import the implicit methods (here: org.okcjug.typeerasure.arrrgh.util.DisjointType._). Each of the the compared methods from the top of this article also required importing the implicit methods. However, I think my approach shows more promise because the implicit defs that need to be imported are not specific to the pseudo-polymorphic methods I'm implementing. In fact, the content of this import is suitable to be included in the Predef object.

Maybe I'll poke around the scala mailing list and see if any library maintainers want to oblige me...

Lazy initialization of blog posts

Well, it has been a couple of years since my last post and we did not get the opportunity to use Scala at work. I lost interest and started chasing other shiny, twinkly things.

But, I did get a chance to give a presentation on Scala at the local JUG. I'll post slides when it is done if I don't completely embarrass myself.