给定一个像这样或那样的签名:
def foo[A, F[_]](implicit mon: Monoid[F[A]], pr: Pure[F]): F[A]
假设 A 是Char
,有没有办法得到 aString
而不是 a List[Char]
?
String
不接受类型参数,所以我认为这是不可能的。下一个最佳选择是什么?现在,我mkString
在结果上使用,但感觉不是最佳的。
我认为String
是一个带有零 ""
和附加 +
的幺半群......
可以说服 String 伪装成更高种类的类型,从而允许使用 形式的函数foo
。然而,Scala 的类型推断目前还不能胜任推断foo
类型参数的工作,因此您必须明确地提供它们,
// 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
请注意,Char
类型参数foo
是未使用的,可以是任何东西,但必须是某种东西:在这种情况下,要么Char
是自然选择,Nothing
要么Any
也可以。
请注意,此解决方案以String
s 的特殊特性为基础:所有类型的值都可以转换为String
s ,因此对所有类型都pure[A](a : => A) : String
可以实现A
。将这个习语复制到其他类型String
很可能必须利用一些机制来在主体中实现特定于类型的情况pure
(例如,某种模式匹配)。
我能想到的最佳解决方案是定义从List[Char]
to的隐式转换String
。
您的分析,scala 的类型系统将拒绝 String ,因为它不是“更高种类的类型” * -> * 是正确的。也就是说,F[_]
任何 F 都不能分配 String 类型。您可以尝试(我没有检查过)隐式转换...
def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], FA_Is_That: F[A] <%< That)
...但是我怀疑这不会那么有用,因为您必须在需要时提供自己的定制转换,而且还因为性能会很糟糕,假设这是代码的热门部分。
或者,使用标准库,您可以使用CanBuildFrom
机器,但它与 scalaz 样式类型类的混合效果远非显而易见。
def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], b: CanBuildFrom[A, F[A], That]): That
当然,在方法的主体中,您将需要使用构建器来构造返回值,而不是 Monoid/Pure 类型类,我怀疑它们会使它们有些多余。