开发者

String seen as a Monoid

开发者 https://www.devze.com 2023-04-10 09:33 出处:网络
Given a signature like this one or that one: def foo[A, F[_]](implicit mon: Monoid[F[A]], pr: Pure[F]): F[A]

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 Strings 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消