仅为 class 标记的参数实现仿函数映射
Implementing functor map for class-tagged arguments only
我有以下数据结构:
class MyDaSt[A]{
def map[B: ClassTag](f: A => B) = //...
}
我想实现一个 Functor
实例,以便能够使用临时多态性。明显的尝试如下:
implicit val mydastFunctor: Functor[MyDaSt] = new Functor[MyDaSt] {
override def map[A, B](fa: MyDaSt[A])(f: A => B): MyDaSt[B] = fa.map(f) //compile error
}
它显然无法编译,因为我们没有提供隐式 ClassTag[B]
。但是是否可以仅将 map
与函数 f: A => B
一起使用,这样就有 ClassTag[B]
。否则编译错误。我的意思是这样的:
def someFun[A, B, C[_]: Functor](cc: C[A], f: A => B) = cc.map(f)
val f: Int => Int = //...
val v: MyDaSt[Int] = //...
someFunc(v, f) //fine, ClassTag[Int] exists and in scope
无论如何我都无法更改它的实现,但我可以创建包装器(看起来没有帮助)或继承。我可以免费使用任何版本的shapeless
。
我目前认为无形是这种情况下的一种方式...
我将详细说明触及的评论:
函子
cats.Functor
在 Scala 类型 的类别中描述了一个 内函子 - 也就是说,您应该能够 map
使用函数 A => B
其中 A
和 B
必须支持任何 Scala 类型。
您拥有的是一个数学函子,但属于具有 ClassTag
的不同的较小类型类别。这些通用仿函数有点不常见——我认为对于 stdlib 类型,只有 SortedSet
可以作为一类有序事物的仿函数——所以它现在在 Scala FP 中是相当未开发的领域,只是在 Scalaz 8 中有所传言。
Cats 没有任何工具来抽象这些东西,因此您不会获得任何实用方法和生态系统支持。如果您想自己滚动,可以使用
科约内达
Coyoneda
可以从任何类型构造函数 F[_]
对 Scala 类型创建一个内函子。思路很简单:
- 有一些初始值
F[Initial]
- 有函数
Initial => A
- 到
map
和A => B
,你不触及初始值,只是简单地组合函数得到Initial => B
您可以将任何 F[A]
提升到 cats.free.Coyoneda[F, A]
。问题是如何把F[A]
弄出来。
如果F
是一个cats.Functor
,那么你可以使用它的原生map
是完全自然的,事实上,结果与根据函子定律 (x.map(f).map(g) <-> x.map(f andThen g)
).
,使用 Coyoneda
并直接使用 F
您的情况并非如此。但是你可以撕开 cats.free.Coyoneda
并委托给你自己的 map
:
def coy[A](fa: MyDaSt[A]): Coyoneda[MyDaSt, A] = Coyoneda.lift(fa)
def unCoy[A: ClassTag](fa: Coyoneda[MyDaSt, A]): MyDaSt[A] =
fa.fi.map(fa.k) // fi is initial value, k is the composed function
这将使您可以使用期望 cats.Functor
:
的函数
def generic[F[_]: Functor, A: Show](fa: F[A]): F[String] = fa.map(_.show)
unCoy(generic(coy(v))) // ok, though cumbersome and needs -Ypartial-unification on scala prior to 2.13
(可运行示例 on scastie)
一个明显的限制是你需要在任何你想调用 unCo
的地方有一个 ClassTag[A]
- 即使你不需要它来 create 首先是 MyDaSt[A]
的实例。
不太明显的一点是,您不会自动获得没有行为差异的保证。是否可以取决于您的 map
做什么 - 例如如果它只是分配一些 Array
s,它应该不会引起问题。
我有以下数据结构:
class MyDaSt[A]{
def map[B: ClassTag](f: A => B) = //...
}
我想实现一个 Functor
实例,以便能够使用临时多态性。明显的尝试如下:
implicit val mydastFunctor: Functor[MyDaSt] = new Functor[MyDaSt] {
override def map[A, B](fa: MyDaSt[A])(f: A => B): MyDaSt[B] = fa.map(f) //compile error
}
它显然无法编译,因为我们没有提供隐式 ClassTag[B]
。但是是否可以仅将 map
与函数 f: A => B
一起使用,这样就有 ClassTag[B]
。否则编译错误。我的意思是这样的:
def someFun[A, B, C[_]: Functor](cc: C[A], f: A => B) = cc.map(f)
val f: Int => Int = //...
val v: MyDaSt[Int] = //...
someFunc(v, f) //fine, ClassTag[Int] exists and in scope
无论如何我都无法更改它的实现,但我可以创建包装器(看起来没有帮助)或继承。我可以免费使用任何版本的shapeless
。
我目前认为无形是这种情况下的一种方式...
我将详细说明触及的评论:
函子
cats.Functor
在 Scala 类型 的类别中描述了一个 内函子 - 也就是说,您应该能够 map
使用函数 A => B
其中 A
和 B
必须支持任何 Scala 类型。
您拥有的是一个数学函子,但属于具有 ClassTag
的不同的较小类型类别。这些通用仿函数有点不常见——我认为对于 stdlib 类型,只有 SortedSet
可以作为一类有序事物的仿函数——所以它现在在 Scala FP 中是相当未开发的领域,只是在 Scalaz 8 中有所传言。
Cats 没有任何工具来抽象这些东西,因此您不会获得任何实用方法和生态系统支持。如果您想自己滚动,可以使用
科约内达
Coyoneda
可以从任何类型构造函数 F[_]
对 Scala 类型创建一个内函子。思路很简单:
- 有一些初始值
F[Initial]
- 有函数
Initial => A
- 到
map
和A => B
,你不触及初始值,只是简单地组合函数得到Initial => B
您可以将任何 F[A]
提升到 cats.free.Coyoneda[F, A]
。问题是如何把F[A]
弄出来。
如果F
是一个cats.Functor
,那么你可以使用它的原生map
是完全自然的,事实上,结果与根据函子定律 (x.map(f).map(g) <-> x.map(f andThen g)
).
Coyoneda
并直接使用 F
您的情况并非如此。但是你可以撕开 cats.free.Coyoneda
并委托给你自己的 map
:
def coy[A](fa: MyDaSt[A]): Coyoneda[MyDaSt, A] = Coyoneda.lift(fa)
def unCoy[A: ClassTag](fa: Coyoneda[MyDaSt, A]): MyDaSt[A] =
fa.fi.map(fa.k) // fi is initial value, k is the composed function
这将使您可以使用期望 cats.Functor
:
def generic[F[_]: Functor, A: Show](fa: F[A]): F[String] = fa.map(_.show)
unCoy(generic(coy(v))) // ok, though cumbersome and needs -Ypartial-unification on scala prior to 2.13
(可运行示例 on scastie)
一个明显的限制是你需要在任何你想调用 unCo
的地方有一个 ClassTag[A]
- 即使你不需要它来 create 首先是 MyDaSt[A]
的实例。
不太明显的一点是,您不会自动获得没有行为差异的保证。是否可以取决于您的 map
做什么 - 例如如果它只是分配一些 Array
s,它应该不会引起问题。