2012-03-20 80 views
1

我有以下代码定义类型类。类型类和子类型

trait Foo[T] { 
    def toFoo(x: T): String 
} 

trait Foos { 
    def toFoo[T](f: T => String): Foo[T] = new Foo[T] { 
    def toFoo(x: T): String = f(x) 
    } 
} 

object Foo extends Foos { 
    def toFoo[A: Foo](a: A) = implicitly[Foo[A]].toFoo(a) 
    implicit def AToFoo: Foo[A] = toFoo { c => 
    "A" 
    } 
    implicit def BToFoo[T]: Foo[B] = toFoo { c => 
    "B" 
    } 

    implicit def ListToFoo[T: Foo]: Foo[List[T]] = toFoo { c => 
    c.map(toFoo(_)). 
    } 
} 

class A 
class B extends A 

现在如果我有,如果我做toFoo(List(new A, new B)我得到List("A", "A")而不是List("A", "B")。如何确保使用BtoFoo方法,而不是AToFoo用于类型B的类?

+0

你期望'List(new A,new B)'有什么类型? – 2012-03-20 13:59:02

+0

我希望'List(new A,new B)'有类型'List [A]' – Stephan 2012-03-22 09:16:48

+0

在这种情况下,当您将'toFoo'映射到'List [A]'上时,它显然首先执行'Flicit [Foo [A]]'并获取'AToFoo'隐式def,并将其用于每个元素。类型类专为类型定向调度而设计,而不是用于价值定向调度,所以它们并不总是很好地与子类型匹配。 – 2012-03-22 18:07:27

回答

2

隐式解析是纯粹的编译时机制。在这里区分子类型的简单方法是在隐式内部进行匹配。完全删除BToFoo,并有代替A版本处理这两种情况下

implicit val AIsFoo : Foo[A] = toFoo { 
    case b: B => "B" 
    case a: A => "A" 
} 

当然,也可以委托给一个方法的层次结构的一部分。您可以委托A中的一个方法,在B中使用overidde,并且仍然具有List的类型类型工作,您无法添加方法。

您也可以考虑宣布Foo逆变。

+0

我已经使用模式匹配实现了你的建议,但是我不得不在'AIsFoo'中检查'A'的所有子节点,而不是直接为任何子节点'X'写入'XIsFoo' A. 宣称'Foo'逆变是如何工作的?我试过 '特质Foo [-T] {def toFoo(x:T){: } – Stephan 2012-03-22 09:20:13