9

I have a class which looks like this:

class X[A <: Throwable, B, C](b: B, c: C)

A, B and C can be inferred, so I can just instantiate it with:

val x = new X(3, 4)

which gives me an X[Nothing, Int, Int] - often what I want.

but I sometimes want to specify A to be something other than Nothing (say AssertionError). Is this possible without also specifying B and C. I imagined syntax along the lines of:

val x = new X[AssertionError](3, 4)
val x = new X[AssertionError, _, _](3, 4)
val x = new X[AssertionError,,](3, 4)

but obviously this doesn't work.

Is there some syntax for this, or some way I can achieve the same result?

Draemon
  • 33,955
  • 16
  • 77
  • 104

5 Answers5

5

My main concern was to make this easy at the point of use (I'd prefer not to have to define new types for each use, as the exceptions types are often different). I found that I can use the companion object to factory an intermediate factory:

class X[A <: Throwable, B, C](b: B, c: C) {
}

trait XFactory[A <: Throwable] {
  def apply[B, C](b: B, c: C): X[A, B, C]
}

object X {
  def apply[A <: Throwable: Manifest](): XFactory[A] = {
    new XFactory[A] {
      override def apply[B, C](b: B, c: C): X[A, B, C] = {
        new X(b, c)
      }
    }
  }
}

val x = X[AssertionError].apply(3,3)

The only downside I can see is that you have to spell out the "apply".

Draemon
  • 33,955
  • 16
  • 77
  • 104
4

Here is my solution:

scala> class X[A <: Throwable, B, C](b: B, c: C)
defined class X

scala> class Builder[A <: Throwable] {
     |   def apply[B, C](b: B, c: C) = new X[A,B,C](b,c)
     | }
defined class Builder

scala> def X[A <: Throwable]: Builder[A] = new Builder[A]
X: [A <: Throwable]=> Builder[A]

scala> val x = X[AssertionError](3, 4)
x: X[AssertionError,Int,Int] = X@2fc709
Eastsun
  • 18,526
  • 6
  • 57
  • 81
3

If you're not scared of terse hardcore syntax, you might want to use type lamdas for this:

Welcome to Scala version 2.10.0-20121205-235900-18481cef9b (OpenJDK 64-Bit Server VM, Java 1.7.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> case class X[A <: Throwable, B, C](b: B, c: C)
defined class X

scala> type P[A,B] = ({type l[a,b] = X[AssertionError, a, b]})#l[A,B]
defined type alias P

scala> val x = new P(1,2)
x: X[AssertionError,Int,Int] = X(1,2)

Still, defining a type alias, as Frank S. Thomas suggested, is a way to go here.

Community
  • 1
  • 1
George
  • 8,368
  • 12
  • 65
  • 106
  • And the great thing about it is that you can still explicitly specify the remaining type parameters: `val x = new P[Int, Long](1, 2)` will compile and correctly be type as `X[AssertionError,Int,Long]` instead of the default `X[AssertionError,Int,Int]` – Régis Jean-Gilles Mar 01 '13 at 12:32
  • 1
    It looks like you're using a snapshot version of Scala 2.10... You might want to get the 2.10.0 final release! – Randall Schulz Mar 01 '13 at 14:45
  • I'm wondering if there are any practical differences between your type alias `P` and my `XAssertionError`. Aren't they identical for all practical purposes? – Frank S. Thomas Mar 02 '13 at 10:43
  • 1
    @FrankS.Thomas When using both as type aliases, there is no difference indeed (other than yours is more readable). Otherwise, there is one difference: type lambdas may be used as types, without additional aliases. E. g. declaring an `Either` monad as `Monad[({type l[α] = Either[A, α]})#l]` *VS* first creating a type alias, and then declaring a `Monad` instance for it. – George Mar 02 '13 at 17:16
3

You could define a type alias where your first type parameter is fixed to AssertionError:

scala> class X[A <: Throwable, B, C](b: B, c: C)
defined class X

scala> type XAssertionError[B, C] = X[AssertionError, B, C]
defined type alias XAssertionError

scala> val x = new XAssertionError(3,4)
x: X[java.lang.AssertionError,Int,Int] = X@22fe135d
Frank S. Thomas
  • 4,725
  • 2
  • 28
  • 47
  • +1. This is cool, but if I I'm specifying a different exception type at each point of use, it's a bit verbose. – Draemon Mar 01 '13 at 12:25
  • This is pretty much what I suggested, adding a comment instead of a separate solution would have been a kind gesture. Gonna delete my answer for clarity... – bluenote10 Mar 01 '13 at 12:30
  • @bluenote10 You suggested a subclass which is more heavyweight (exists at runtime). The type alias solution is notably cleaner (is resolved at compile time) – Draemon Mar 01 '13 at 12:49
  • I'm totally with you there (although there might be cases where subclassing might be desirable?). That is why I deleted my answer. But that is nothing that can't be solved by an edit/comment... – bluenote10 Mar 01 '13 at 13:05
1

You could just define a constructor with a default argument.

scala> class X[A <: Throwable, B, C](b: B, c: C, clz:Class[_ <: A] = classOf[Nothing])
defined class X

scala> new X(1,2)
res0: X[Nothing,Int,Int] = X@16de4e1

scala> new X(1,2, classOf[AssertionError])
res1: X[AssertionError,Int,Int] = X@1e41869
Emil L
  • 20,219
  • 3
  • 44
  • 65