Scala 定义函子时,如何抽象第二个类型参数?
使用Scala和Cats学习地道的FP。我有以下代码Scala 定义函子时,如何抽象第二个类型参数?,scala,functional-programming,functor,scala-cats,Scala,Functional Programming,Functor,Scala Cats,使用Scala和Cats学习地道的FP。我有以下代码 package org.economicsl.mechanisms import cats._ import cats.implicits._ trait Preference[-A] { self => def compare(a1: A, a2: A): Int final def ordering[A1 <: A]: Ordering[A1] = { new Ordering[A1] {
package org.economicsl.mechanisms
import cats._
import cats.implicits._
trait Preference[-A] {
self =>
def compare(a1: A, a2: A): Int
final def ordering[A1 <: A]: Ordering[A1] = {
new Ordering[A1] {
def compare(a1: A1, a2: A1): Int = {
self.compare(a1, a2)
}
}
}
}
object Preference {
implicit val contravariant: Contravariant[Preference] = {
new Contravariant[Preference] {
def contramap[A, B](fa: Preference[A])(f: B => A): Preference[B] = {
new Preference[B] {
def compare(b1: B, b2: B): Int = {
fa.compare(f(b1), f(b2))
}
}
}
}
}
/** Defines a preference for a particular alternative. */
def particular[A](alternative: A): Preference[A] = {
new Preference[A] {
def compare(a1: A, a2: A): Int = {
if ((a1 != alternative) & (a2 == alternative)) {
-1
} else if ((a1 == alternative) & (a2 != alternative)) {
1
} else {
0
}
}
}
}
}
/** Base trait defining a generic social welfare function.
*
* A social welfare function aggregates the preferences of individual agents
* into a common preference ordering.
*/
trait SocialWelfareFunction[-CC <: Iterable[P], +P <: Preference[A], A]
extends (CC => P)
/** Companion object for the `SocialWelFareFunction` trait. */
object SocialWelfareFunction {
val setInvariant: Invariant[({ type F[A] = SocialWelfareFunction[Set[Preference[A]], Preference[A], A] })#F] = {
new Invariant[({ type F[A] = SocialWelfareFunction[Set[Preference[A]], Preference[A], A] })#F] {
def imap[A, B](fa: SocialWelfareFunction[Set[Preference[A]], Preference[A], A])(f: A => B)(g: B => A): SocialWelfareFunction[Set[Preference[B]], Preference[B], B] = {
new SocialWelfareFunction[Set[Preference[B]], Preference[B], B] {
def apply(preferences: Set[Preference[B]]): Preference[B] = {
fa(preferences.map(pb => pb.contramap(f))).contramap(g)
}
}
}
}
}
val seqInvariant: Invariant[({ type F[A] = SocialWelfareFunction[Seq[Preference[A]], Preference[A], A] })#F] = {
new Invariant[({ type F[A] = SocialWelfareFunction[Seq[Preference[A]], Preference[A], A] })#F] {
def imap[A, B](fa: SocialWelfareFunction[Seq[Preference[A]], Preference[A], A])(f: A => B)(g: B => A): SocialWelfareFunction[Seq[Preference[B]], Preference[B], B] = {
new SocialWelfareFunction[Seq[Preference[B]], Preference[B], B] {
def apply(preferences: Seq[Preference[B]]): Preference[B] = {
fa(preferences.map(pb => pb.contramap(f))).contramap(g)
}
}
}
}
}
}
package org.economicsl.mechanism
进口猫_
进口猫_
特质偏好[-A]{
自我=>
def比较(a1:A,a2:A):整数
最终def订购[A1 A]:首选项[B]={
新优惠[B]{
def比较(b1:B,b2:B):整数={
fa.比较(f(b1),f(b2))
}
}
}
}
}
/**定义特定备选方案的首选项*/
定义特定[A](备选方案:A):首选项[A]={
新优惠[A]{
def比较(a1:A,a2:A):整数={
如果((a1!=备选方案)和(a2==备选方案)){
-1
}否则如果((a1=备选方案)和(a2!=备选方案)){
1.
}否则{
0
}
}
}
}
}
/**定义一般社会福利函数的基本特征。
*
*社会福利函数集合了个体代理人的偏好
*变成一个共同的偏好排序。
*/
特质社会福利函数[-CC B)(g:B=>A):社会福利函数[Set[Preference[B]],Preference[B],B]={
新的社会福利函数[Set[Preference[B]],Preference[B],B]{
def应用(首选项:设置[首选项[B]]):首选项[B]={
fa(preferences.map(pb=>pb.contramap(f))).contramap(g)
}
}
}
}
}
val Seq不变量:不变量[({type F[A]=社会福利函数[Seq[Preference[A]],Preference[A],A]})#F]={
新的不变量[({type F[A]=社会福利函数[Seq[Preference[A]],Preference[A],A]})#F]{
定义imap[A,B](fa:SocialWelfalFunction[Seq[Preference[A]],Preference[A],A])(f:A=>B)(g:B=>A):SocialWelfalFunction[Seq[Preference[B]],Preference[B],B]={
新的社会福利函数[Seq[Preference[B]],Preference[B],B]{
def应用(首选项:顺序[首选项[B]]):首选项[B]={
fa(preferences.map(pb=>pb.contramap(f))).contramap(g)
}
}
}
}
}
}
在SocialWelfareFunction
的伴随对象中,我定义了两个不变函子。实现几乎相同:第一个函子使用Set
存储首选项[a]
实例,第二个函子使用Seq
有没有办法对存储首选项的集合类型进行抽象?如果解决方案需要,我愿意包含其他类型级别的依赖项。您可以这样做:
def invariant[C[X] <: Iterable[X] : Functor]: Invariant[({ type F[A] = SocialWelfareFunction[C[Preference[A]], Preference[A], A] })#F] = {
new Invariant[({ type F[A] = SocialWelfareFunction[C[Preference[A]], Preference[A], A] })#F] {
def imap[A, B](fa: SocialWelfareFunction[C[Preference[A]], Preference[A], A])(f: A => B)(g: B => A): SocialWelfareFunction[C[Preference[B]], Preference[B], B] = {
new SocialWelfareFunction[C[Preference[B]], Preference[B], B] {
def apply(preferences: C[Preference[B]]): Preference[B] = {
fa(Functor[C].map(preferences)(pb => pb.contramap(f))).contramap(g)
}
}
}
}
}
令人恼火的是,它们似乎没有用于集合
和序列
的内置函子
,但它们很容易定义自己:
private[this] implicit val setFunctor = new Functor[Set] {
override def map[A, B](s: Set[A])(f: A => B): Set[B] = s.map(f)
}
val setInvariant = invariant[Set]
什么是
首选项
,为什么它上面有contracmap
?是否可以添加一些存根实现以使代码段可编译?能否解释类型参数限制[C[X]C
必须是Iterable
的子类,因为SocialWelfareFunction
的类型限制。然后:Functor
确保您有一个隐式Functor[C]
,它使您能够映射到C
类型。如果没有它,您可以尝试使用Iterable
的map
方法,但随后您会得到一个Iterable
返回,而不是C
。可能有一种方法可以使用标准库的CanBuildFrom
东西来实现,但因为您已经深入到cats,我认为这样做更容易/更干净。明白。我更喜欢您的解决方案,而不是使用隐式cbf:CanBuildFrom
构造,因为我认为新的集合API将来将不再使用CanBuildFrom
。
private[this] implicit val setFunctor = new Functor[Set] {
override def map[A, B](s: Set[A])(f: A => B): Set[B] = s.map(f)
}
val setInvariant = invariant[Set]