字符串被视为一个幺半群

5

假设有这样一个签名或者那个签名:

def foo[A, F[_]](implicit mon: Monoid[F[A]], pr: Pure[F]): F[A]

假设A是Char类型,是否有一种方法可以得到一个String而不是List [Char]?
由于String类型没有类型参数,所以我认为这是不可能的。那么下一个最佳选项是什么?目前,我使用结果上的mkString,但感觉这不是最理想的选择。
我认为String是一个具有“零”“”和“附加”+的幺半群...

为什么你需要 Monoid[F[A]] 而不是只有 Monoid[F]? - CheatEx
@CheatEx,在这种情况下,我不是编写foo的人,我只是调用者。 - huynhjl
3个回答

7

可以让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

请注意,在 foo 中,Char 类型的参数未使用,但必须是某个值:在这种情况下,选择 Char 是自然的选择,但 NothingAny 也可以。

请注意,此解决方案利用了 String 的特殊特性:所有类型的值都可以转换为 String,因此对于所有类型 Apure[A](a: => A) : String 是可实现的。要想复制这种习惯用法以适用于除 String 以外的类型,很可能必须利用一些机制来在 pure 的主体中实现特定于类型的情况(例如某种模式匹配)。


2
但是 implicitly[Pure[ConstString#λ]].pure('a') 将返回空字符串,所以这并没有什么用处。不是吗? - oxbow_lakes
当然,您需要一个不同的Pure [ConstString#λ]的定义来完成任何有用的工作。回答已经修改以反映这一点。 - Miles Sabin
@DanielC.Sobral 确实是你 :-) - Miles Sabin
我建议使用a.asInstanceOf[String]来反映StringPure有点作弊的事实。 - retronym
@retronym,我同意这是由于String的特殊性质而产生的特例。我已经编辑了答案以反映这一点,但我认为坚持使用.asInstanceOf[String]有点过分了;-) - Miles Sabin

6
我能想到的最好解决方案是,定义一个从 List[Char]String 的隐式转换。

0

你的分析是正确的,Scala的类型系统会拒绝String作为“高阶类型”的一种。也就是说,对于任何F,类型String都不能赋值给F[_]。你可以尝试(我没有检查过)使用隐式转换...

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 类型类,这使它们有些多余,我认为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接