Reputation: 19
I have a very simple class:
case class Foo[+T](t: T)
Now I want to add a argument to transform T to Int. For specific reasons, I do not want to use a typeclass, an implicit or any inheritance based solution because this is really what I am modeling: a class containing some data and the function to transform it.
So I write
case class Foo[+T](t: T, f: T => Int)
or
case class Foo[+T](t: T, f: Function1[T, Int])
Of course, it doesn't work because f is contravariant on T. Any solution?
Upvotes: 0
Views: 97
Reputation: 51658
You could try existential type
case class Foo[+T](t: T, f: (_ <: T) => Int)
but actually (_ <: T) => Int
is just Nothing => Int
.
(In Dotty it's also possible to have case class Foo[+T](t: T, f: [U <: T] => U => Int)
.)
Consider adding one more type parameter
case class Foo[+T, U <: T](t: T, f: U => Int)
Then you can use "partially applied" pattern when you want U
to be inferred
def mkFoo[T] = new PartiallyApplied[T]
class PartiallyApplied[T] {
def apply[U <: T](t: T, f: U => Int) = Foo(t, f)
}
trait Parent
case class Child(i: Int) extends Parent
mkFoo[Parent](new Parent {}, (c: Child) => c.i)
One more option is to make U
a type member
trait Foo[+T] {
type U <: T
val t: T
val f: U => Int
}
object Foo {
def apply[T, _U <: T](_t: T, _f: _U => Int): Foo[T] { type U = _U } = new Foo[T] {
override type U = _U
override val t: T = _t
override val f: U => Int = _f
}
def unapply[T](foo: Foo[T]): Option[(T, foo.U => Int)] = Some((foo.t, foo.f))
}
Maybe your class can be
case class Foo[+T](t: T) {
def f[U >: T](t1: U): Int = ???
}
Otherwise it's just invariant case class Foo[T](t: T, f: T => Int)
.
Upvotes: 2