I have a situation where none of the solutions that I'm aware of feel like good ones. I am trying to define a typeclass, like the example below, where it has an abstract type S
that must implement another typeclass (not shown) Valid[A]
. It feels like the easiest way to do this would be to create an apply
constructor, which will force the user to enter S
and automatically bring in an implicit instance for sIsValid
.
The problem with this is that function changeType
takes a type parameter. Despite some googling I have not figured out how to write a type annotation for a function that takes a type parameter (this may be because it seems like Scala does not allow anonymous functions with type parameters). Does my approach here seem like a sensible one? If I can provide a type annotation for changeType
then the typeclass user can still pass a non-anonymous function to the apply
constructor, which seems like a mostly satisfactory solution.
abstract class IsTC[A[_], T] {
// type S is an abstract type rather than type parameter, but must implement Valid[A]
type S
implicit val sIsValid: Valid[S]
def get(self: A[T], i: Int): T
def changeType[_T]: A[_T]
}
object IsTC {
def apply[A[_], T, _S](
fget: (A[T], Int) => T,
fchangeType: // what should this type annotation be?
): IsTC[A, T] { type S = _S } = new IsTC[A, T] {
type S = _S
def get(self: A[T], i: Int) = fget(self, i)
def changeType[_T]: A[_T] = fchangeType[_T]
}
}
Any help/thoughts gratefully received.
Scala 2 doesn't support polymorphic functions. Polymorphic can be methods, not values. And functions are values. Polymorphic functions can be emulated with wrappers
// for [A] => (a: A) => B[A]
trait Poly {
def apply[A](a: A): B[A]
}
or
// for [A <: U] => (a: A) => B[A]
trait Poly[U] {
def apply[A <: U](a: A): B[A]
}
like shapeless.Poly
. They generalize ordinary functions
trait Function[A, B] {
def apply(a: A): B
}
Try
object IsTC {
def apply[A[_], T, _S](
fget: (A[T], Int) => T,
fchangeType: FchangeType[A]
): IsTC[A, T] { type S = _S } = new IsTC[A, T] {
override type S = _S
override implicit val sIsValid: Valid[_S] = ???
override def get(self: A[T], i: Int) = fget(self, i)
override def changeType[_T]: A[_T] = fchangeType[_T]
}
}
trait FchangeType[A[_]] {
def apply[X]: A[X]
}
Dotty has polymorphic functions [A <: U] => (a: A) => f[A](a)
of polymorphic function types [A <: U] => A => B[A]
but there is implementation restriction: polymorphic function types must have a value parameter
so you can't have polymorphic function type [X] => A[X]
(not to be confused with a type lambda [X] =>> A[X]
) currently even there.
Also (besides emulation with wrappers) some polymorphic types can be expressed via existential types: [A] => B[A] => C
(for constant C
) is actually B[_] => C
.
This is because ∀a: (B(a) => C)
≡ (∃a: B(a)) => C
.