2013-06-27 3 views
6

Я работаю над небольшой библиотекой для экономических моделей, которая проверяет единицы объектов, используя типы, например. вместо val apples = 2.0 пишем val apples = GoodsAmount[KG, Apples](2.0). Для создания пакета товаров я пытаюсь использовать HLists из бесформенной библиотеки. Это прекрасно работает, но в некоторых случаях я не могу быть таким же общим кодом, как я предпочел. См. следующая проблема.Безшовное: проверка типов ограничений полиморфных функций

Я начинаю с простого кода, который объясняет, что я хочу снять в бесформенный. Мы создаем два класса, на которых изображены Km, другие Мили. Должно быть разрешено добавлять классы Km, но не мили. То, что я использую абстрактный тип T, в основном мотивируется нашей более сложной библиотекой. И косвенный вызов функции «+» заключается только в том, что нам нужно что-то подобное в бесформенном случае.

trait Foo { 
    type T 
    val v: Double 
    def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v 
} 

trait _Km 
trait _Miles 

case class Km(v: Double) extends Foo { type T = _Km } 
case class Miles(v: Double) extends Foo { type T = _Miles } 

object ExampleSimple extends App { 
    def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b } 

    add(Km(1), Km(2)) 
    // add(Km(1), Miles(2)) /* does not compile as intended */ 
} 

Это работает по назначению. Но для функции «добавить» необходимо иметь проверку типа Contraint. Моя попытка распространить это на HLists выглядит следующим образом:

object ExampleShapeless extends App { 
    import shapeless._ 

    val l1 = Km(1) :: Km(2) :: HNil 
    val l2 = Km(4) :: Km(3) :: HNil 

    object add extends Poly1 { 
    implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b } 
    } 

    (l1 zip l2).map(add) 
} 

Но генерировать следующее сообщение об ошибке (с помощью Scala 2.10.2):

[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T. 
[error]  implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b } 
[error]                  ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]] 
[error] (l1 zip l2).map(add) 

должен быть закреплен Первая ошибка, в случае что я могу добавить ограничение типа к функции caseTuple, но, честно говоря, я не понял, как работает функция at и где я могу добавить параметр неявных доказательств. И я также не знаю, что я должен делать, чтобы Mapper нашел его неявное значение.

Менее общий вариант, где я replase функцию caseTuple с

implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b } 

работает отлично, но нужно будет написать много избыточного кода (хорошо, это решение было бы еще лучше, так как наше нынешнее решение с использованием Кортежи). Может ли кто-нибудь дать мне подсказку, как я могу решить эту проблему?

Спасибо, KLINKE

+0

Вы можете попытаться определить ваш 'Foo' следующим образом:' trait Foo [T <: Foo] {v: Double; + (t T): T = ...} '. 'class Km (val v: Double) расширяет Foo [Km]'. 'implicit def add [T] = at [(Foo [T], Foo [T])]' – senia

ответ

7

Вы можете требовать от членов типа, чтобы соответствовать путем добавления параметра типа в случае:

object add extends Poly1 { 
    implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] { 
    case (a, b) => a + b 
    } 
} 

Или вы могли бы использовать экзистенциальный тип, так как вы только действительно важно, что они одинаковы:

object add extends Poly1 { 
    implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] = 
    at[(A, A)] { 
     case (a, b) => a + b 
    } 
} 

Любая версия обеспечит поведение, которое вы хотите.

+0

Спасибо, это отлично работает и в моем более сложном случае ;-) Но у меня есть проблема с отсутствующим неявным значением для Mapper. Я постараюсь решить ее сам, но, может быть, вы тоже можете мне помочь здесь? – Klinke

+0

Хорошо, нашел решение для упрощенной версии, добавив, что контекстная привязка для A помогает. Итак, у меня есть теперь 'implicit def caseTuple [_t, A <: Foo {type T = _T} <% Foo {type T = _T}] = ...' На этот раз решение не так легко переводится в мою полную версию , но надеюсь, что я смогу исправить и новые проблемы. – Klinke

+0

@Klinke: Я не уверен, что понимаю проблему - если я скопирую и вставляю свой 'add' в ваш' ExampleShapeless', все работает так, как ожидалось. –