2014-05-25 2 views
2

경로에 대한 typeclass가 있습니다. 여기에는 확대 메소드 (경로가 정점을 중심으로 확대되어 확대됨)가 포함되어 있습니다.이 메소드는 경로 유형 클래스에 다시 부합하는 모든 유형을 반환 할 수 있어야합니다. 나는 다음과 같은 오류 얻을 그러나메서드에서 typeclass를 사용하는 개체 반환

case class Circle() 
case class Polyline() 
trait Path[T] { 
    /// Note how the return type T2 should again have a Path[T2] typeclass 
    def enlarge[T2 : Path](path : T) : T2 
} 

object Path { 
    implicit object PathCircle extends Path[Circle] { 
    def enlarge[T2 : Path](path : Circle) = Polyline() 
    } 
    implicit object PathPolyline extends Path[Polyline] { 
    def enlarge[T2 : Path](path : Polyline) = Polyline() 
    } 
} 

object Test { 
    import Path._ 

    implicitly[Path[Circle]].enlarge(Circle()) 
} 

:

[error] test.scala:12: type mismatch; 
[error] found : Polyline 
[error] required: T2 
[error] Note: implicit object PathPolyline is not applicable here because it comes after the application point and it lacks an explicit result type 
[error]  def enlarge[T2 : Path](path : Circle) = Polyline() 
[error]             ^
[error] test.scala:15: type mismatch; 
[error] found : Polyline 
[error] required: T2 
[error]  def enlarge[T2 : Path](path : Polyline) = Polyline() 
[error]              ^
[error] test.scala:22: ambiguous implicit values: 
[error] both object PathPolyline in object Path of type Path.PathPolyline.type 
[error] and object PathCircle in object Path of type Path.PathCircle.type 
[error] match expected type Path[T2] 
[error] implicitly[Path[Circle]].enlarge(Circle()) 
[error]         ^
[error] three errors found 
[error] (compile:compile) Compilation failed 

내가 무슨 일을하고있는 중이 야 나는 이런 일이 (단순 제거 데이터 멤버)를 작동합니다 생각?

+1

발신자가 반환 유형을 선택하게합니다 (예 : PathCircle.enlarge [Circle] (Circle())을 호출하면 어떻게 될까요? –

+0

현재 확대/축소 방법에서 반환 된 폴리 라인을 사용자 지정 유형 (귀하의 경우 Circle)으로 변환 할 수있는 Conv [T] typeclass가 있습니다. 그러나 이것이 올바른 방법이 아니라는 것이 옳습니다. 어쩌면 반환 형식에 Path Typeclass가 있어야한다고 지정하는 다른 방법이 있을까요? – MHOOO

답변

1

많은 시행 착오 끝에 나는 해결할 수있는 해결책을 찾았습니다. 기본적으로 PathBox 객체를 반환 할 수 있습니다. PathBox 객체는 반환 된 Path 인스턴스와 해당 암시 적 Path 형식의 모든 증거에 대한 래퍼 객체입니다. 패스 박스에 대한 실제 typeclass의 구현과 커플을하고, 리턴 된 매개 변수를 얻습니다.이 매개 변수는 다시 구현 된 typeclass를가집니다. 솔루션과 관련된 유일한 문제점은 개별 typeclass 구현이 순환 종속성을 가질 수 없다는 것입니다.하지만 지금은 그와 함께 살 수 있습니다. 코드는 :

case class Circle() 
case class CircleArc() 
case class Polyline() 

/// A container storing both an object adhering to an EnlargablePath 
/// typeclass, plus the instance of the typeclass itself 
case class PathBox[T](val t : T)(implicit val tInst : EnlargablePath[T]) 

/// Any path that can be enlarged and potentially return a new kind of 
/// path, which still adheres to the EnlargablePath typeclass however 
trait EnlargablePath[T] { 
    def enlarge[_](path : T) : PathBox[_] 
} 

object EnlargablePath { 

    // A Polyline can always return a larger polyline 
    implicit object PathPolyline extends EnlargablePath[Polyline] { 
    def enlarge[_](path : Polyline) = PathBox(Polyline()) 
    } 

    // Enlarging a circle only req. a new radius, so it returns a circle 
    implicit object PathCircle extends EnlargablePath[Circle] { 
    def enlarge[_](path : Circle) = PathBox(Circle()) 
    } 

    // Enlarging a CircleArc, results in a full circle. This actually 
    // requires the PathCircle object to be defined before! 
    implicit object PathCircleArc extends EnlargablePath[CircleArc] { 
    def enlarge[_](path : CircleArc) = PathBox(Circle()) 
    } 

    // Make sure that a PathBox[_] can also be treated as an EnlargablePath 
    implicit def PathPathBox[T] = new EnlargablePath[PathBox[T]] { 
    def enlarge[_](pathBox : PathBox[T]) = pathBox.tInst.enlarge(pathBox.t) 
    } 

    // For nicer syntax, any object that implements the EnlargablePath 
    // typeclass gets its methods added. If you care about performance, 
    // take a look at spire's (the scala math library) macros for this 
    // purpose 
    implicit class EnlargablePathOps[T : EnlargablePath](val p : T) { 
    def enlarge = implicitly[EnlargablePath[T]].enlarge(p) 
    } 
} 

object Test { 
    import EnlargablePath._ 

    println(Circle().enlarge) 
    println(Circle().enlarge.enlarge) 
    println(Circle().enlarge.enlarge.enlarge) 
} 
+0

유일한 문제는 'Circle(). enlarge'가'PathBox [_]'유형을 반환한다는 것입니다. 나는 이전에이 일을하고 있었고, 내가 원하는 것을 얻지 못했지만, 곧 내 아이디어를 업로드 할 것이다. – ggovan

0

이는 타입 수준의 프로그래밍에 대한이 이야기 (시청 가치)

Academese to English: Scala's Type System, Dependent Types and What It Means To You

case class Circle() 
case class Polyline() 

// This is our type class. 
// More type parameters could be added if more functions were added. 
trait Path[T,E]{ 
    def enlarge(p:T):E 
} 

object Path { 
    implicit object PathCircle extends Path[Circle,Polyline] { 
    override def enlarge(path : Circle): Polyline = Polyline() 
    } 
    implicit object PathPolyline extends Path[Polyline,PolyLine] { 
    override def enlarge(path : Polyline) = Polyline() 
    } 
    def enlarge[T,E](path:T)(implicit tp:Path[T,E]):E = tp.enlarge(path) 
} 

object Test { 
    import Path._ 

    // The easist way to call it 
    val pl: Polyline = Path.enlarge(Circle()) 

    // Or since we're already importing Path 
    val pl2: Polyline = enlarge(Circle()) 
} 

이 방법의 장점은의 기반으로 우리의 반환 형식은 매우 될 수 있음 특유한. 이것은 좋은 일일 수 있습니다!

trait Path[T] { 
    type E 
    def enlarge(path : T) : E 
} 
... 
implicit object PathCircle extends Path[Circle] { 
    type E = Circle 
    ... 

을하지만 이것은 단지 리턴 타입을 주었다


나는 처음에이 유형의 회원들과 함께 작업을 얻으려고 노력했다.

val pl:Path[Circle]#E = enlarge(Circle()) 
관련 문제