Given a signature like this one or that one:
def foo[A, F[_]](implicit mon: Monoid[F[A]], pr: Pure[F]): F[A]
Assuming A is Char
, is there a way to get a String
instead of a List[Char]
?
String
does not take a type parameter, so I assume it's not possible. What's the next best option? Right now, I use mkString
on the result开发者_JAVA技巧, but that doesn't feel optimal.
I think String
is a monoid with zero ""
and append +
...
It is possible to persuade String to masquerade as a higher-kinded type, and hence allow functions of the form of foo
to be applicable. However, Scala's type inference isn't currently up to the job of inferring foo
's type arguments, so you'll have to supply them explicitly,
// Assuming the the definitions of Pure and Monoid from Scalaz
type ConstString = {
type λ[X] = String
}
implicit def StringPure = new Pure[ConstString#λ] {
def pure[A](a: => A) = a.toString
}
val sm = implicitly[Monoid[String]]
val sp = implicitly[Pure[ConstString#λ]]
val f : String = foo[Char, ConstString#λ](sm, sp) // OK
Note that the Char
type argument to foo
is unused and can be anything, but must be something: in this case either Char
is the natural choice, but Nothing
or Any
would do as well.
Note that this solution trades on String
's special characteristics: values of all types are convertible to String
s so pure[A](a : => A) : String
is implementable for all types A
. Replicating this idiom for types other than String
would most likely have to exploit some mechanism for implementing type-specific cases in the body of pure
(eg. a pattern match of some sort).
The best solution I can think of it is to define an implicit conversion from List[Char]
to String
.
Your analysis, that scala's type-system will reject String as not being a "higher kinded type" * -> * is correct. That is, the type String is not assignable to F[_]
for any F. You could try (I have not checked this) implicit conversions...
def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], FA_Is_That: F[A] <%< That)
...but this will not be that useful I suspect because you'd have to provide your own bespoke conversions where required but also because the performance would be terrible, assuming this is a hot part of the code.
Or, using the standard library, you could use the CanBuildFrom
machinery but it's far from obvious how nicely this will mix with the scalaz-style typeclasses.
def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], b: CanBuildFrom[A, F[A], That]): That
In the body of the method, of course, you will need to use the builder to construct the return value, as opposed to the Monoid/Pure typeclasses, rendering them somewhat redundant, I suspect.
精彩评论