这个问题很难解释。我尝试实现一种Arrow
,将其称为MyArr
,从Arrow
扩展scalaz
特征。但是,MyArr
也是一种Algbraic数据类型,所以我用if创建了几个case类,并写下这样的代码(最小例子):
package org.timeless
import scalaz._
import Scalaz._
import std.option._, std.list._
sealed trait MyArr[A,B] extends Arrow[MyArr] {
def id[A]: MyArr[A,A] = SId()
def compose[A,B,C](s1: MyArr[B,C], s2: MyArr[A,B]): MyArr[A,C] =
SRight(s2, s1)
def arr[A,B](f: A=>B): MyArr[A,B] = SPure(f)
def first[A,B,C](s: MyArr[A,B]): MyArr[(A,C),(B,C)] = SPar(s, SId())
}
case class SId[A] () extends MyArr[A,A]()
case class SRight[M[_],A,B,C](s1: MyArr[A,B], s2: MyArr[B,C]) extends MyArr[A,C]
case class SPure[A,B](f: A=>B) extends MyArr[A,B]
case class SPar[M[_],A,B,C,D](s1: MyArr[A,B], s2: MyArr[C,D]) extends MyArr[(A,C),(B,D)]
object MyArr {
val b = SId[Int]()
val c = SId[Int]()
// val a: MyArr[Int,Int] = id[Int] // Error
// val d = c compose b // Error
// val d = b >>> c // Error
val d = b >>> (b,c) // ??? Arguments
}
很容易看出,id
函数实际上是SId
等的构造函数。类本身编译正常,但我没有看到任何方法将它实际用作{{ {1}}。我实际上来自Haskell编程,所以我可能完全错了,但Haskell中的等效代码应该是这样的(使用GADT):
Arrow
答案 0 :(得分:2)
Scala中的Typeclasses并没有像Haskell那样内置于语言中。它们只是一种设计模式,通常在隐式参数的帮助下实现:
def pure[M[_], T](
implicit monad: Monad[M] // Fetching a monad instance for type M
): M[T] =
monad.pure[T] // Using methods of the monad instance
您不必扩展类型类特征,为某些ADT提供类型类实例。必须在某处将类型类实例定义为implicit val
,implicit def
,implicit object
等。然后,您可以在使用之前将import
相关实例放入范围。
还有一种方法可以自动提供类型类实例,而无需显式导入它。要做到这一点,实例应该在类型类class / trait或ADT class / trait的伴随对象中声明,或者在它们的父类/ trait之一中声明;或者在其中定义了其中一个的包对象中。您可以在此答案中阅读有关隐式解决方案主题的更多信息:Where does Scala look for implicits?
因此,要回到您的问题,您可以像这样定义您的ADT:
sealed trait MyArr[A,B]
case class SId[A] () extends MyArr[A,A]()
case class SRight[M[_],A,B,C](s1: MyArr[A,B], s2: MyArr[B,C]) extends MyArr[A,C]
case class SPure[A,B](f: A=>B) extends MyArr[A,B]
case class SPar[M[_],A,B,C,D](s1: MyArr[A,B], s2: MyArr[C,D]) extends MyArr[(A,C),(B,D)]
并在Arrow
的伴随对象中提供MyArr
实例:
object MyArr {
implicit val arrow: Arrow[MyArr] = new Arrow[MyArr] {
def id[A]: MyArr[A,A] = SId()
def compose[A,B,C](s1: MyArr[B,C], s2: MyArr[A,B]): MyArr[A,C] =
SRight(s2, s1)
def arr[A,B](f: A=>B): MyArr[A,B] = SPure(f)
def first[A,B,C](s: MyArr[A,B]): MyArr[(A,C),(B,C)] = SPar(s, SId())
}
def apply[T]: MyArr[T, T] = arrow.id[T]
}
由于Scala的某些限制,我认为在Scala中无法提供通用的id
函数(或return
,pure
等)。类型推断。
相反,您可以使用上面定义的MyArr.apply
方法:
val a: MyArr[Int,Int] = MyArr[Int]
由于ADT没有扩展类型类,因此它不会继承其compose
或>>>
等方法。可以使用'Pimp My Library' pattern单独提供它们。
实际上,scalaz中的>>>
方法来自ComposeOps
类,其定义如下:
final class ComposeOps[F[_, _],A, B] private[syntax]
(self: F[A, B])
(val F: Compose[F])
extends Ops[F[A, B]]
如果有一个F[_, _]
实例可用于ComposeOps
,则会从某个类型Compose
定义到此类F
的隐式转换:
implicit def ToComposeOps[F[_, _],A, B](v: F[A, B])(implicit F0: Compose[F])
此转换是使用import Scalaz._
导入的,这就是为什么此行有效的原因:
val d = b >>> c
但是scalaz中没有扩展,它在带有compose
实例的ADT上提供Arrow
方法,所以这一行仍然会出错:
val e = c compose b
要使其正常工作,您可以自行提供扩展程序。一个简单的例子可能如下所示:
// implicit class is equivalent to a normal class
// with an implicit conversion to this class
implicit class ArrowOps[T[_, _], A, B](f: T[A, B])(implicit arrow: Arrow[T]) {
def compose[C](g: T[C, A]): T[C, B] = arrow.compose(f, g)
}