Ошибка вывода типа / проверки типа при использовании вычислений на уровне типа

#generics #scala #types #alias #units-of-measurement

#обобщения #scala #типы #псевдоним #единицы измерения

Вопрос:

Я столкнулся с проблемой при работе с функциональностью единиц измерения в метаскале, определенной в файле Units.scala.

Для остальной части этого вопроса я буду использовать упрощенную схему, содержащую только один тип единицы измерения, length .

Итак, где на самом деле выглядит тип

 Quantity[_1, _0, _0, _0, _0, _0, _0] 
          ^   ^   ^   ^   ^   ^   ^
          |   |   |   |   |   |   |
          | Mass  | Crncy.|  Mol  |
       Length   Time    Temp. Lum.Intensity
  

этого будет достаточно для демонстрации проблемы:

 Quantity[_1]
          ^
          |
       Length
  

Как только требуется определить тип, начинаются проблемы.

Рассмотрим этот пример (также взгляните на код из UnitsTest.scala):

 val length: Quantity[_1] = m(5)
val area:   Quantity[_2] = length * length // (1) Works
val dist:   Quantity[_1] = area / length   // (2) Doesn't work!
  

В последней строке я получаю сообщение об ошибке:

 type mismatch;
  found :
    scalax.units.Units.Quantity[
      scalax.units.Subtractables.-[
        scalax.units.Integers._2,
        scalax.units.Integers._1
      ]
    ]

  required:
    scalax.units.Units.Quantity[
      scalax.units.Integers._1
    ]
  

Похоже, что компилятор не может определить, что тип под рукой равен Quantity[_1] , когда «вычитается измерение», например, переход от area к dist, как в (1) :

 Quantity[_2 - _1] <<not equal to>> Quantity[_1]
  

Сбивает с толку то, что это работает при «добавлении измерения», например, при переходе от длины к площади, как в (2) :

 Quantity[_1   _1] <<equal to>> Quantity[_2]
  

(Извините, что не вставил сюда весь код, это слишком много. Я попытался свести к минимуму свой пример, но мне это не удалось. Вот почему я просто ссылаюсь на него.)

Ответ №1:

Тип Sub from Subtractable отсутствует в MInt черте. Простым определением, позволяющим заставить его работать, было бы выполнить отрицательное сложение, когда вы хотите вычесть тип в MSucc и MNeg .

 sealed trait MInt extends Visitable[IntVisitor] with Addable with Subtractable {
  type AddType = MInt
  type SubType = MInt
  type Add[I <: MInt] <: MInt
  type Sub[I <: MInt] <: MInt
  type Neg <: MInt
  type Succ <: MInt
  type Pre <: MInt
}

final class _0 extends Nat {
  type Add[I <: MInt] = I
  type Sub[I <: MInt] = I#Neg
  type AcceptNatVisitor[V <: NatVisitor] = V#Visit0
  type Neg = _0
  type Succ = MSucc[_0]
  type Pre = Succ#Neg
}

final class MSucc[P <: Nat] extends Pos {
  type This = MSucc[P]
  type Add[N <: MInt] = P#Add[N]#Succ
  type Sub[N <: MInt] = Add[N#Neg]
  type AcceptNatVisitor[V <: NatVisitor] = V#VisitSucc[P]
  type Neg = MNeg[This]
  type Pre = P
  type Succ = MSucc[This]
}

final class MNeg[P <: Pos] extends MInt {
  type Add[N <: MInt] = P#Add[N#Neg]#Neg
  type Sub[N <: MInt] = Add[N#Neg]
  type Accept[V <: IntVisitor] = V#VisitNeg[P]
  type Neg = P
  type Succ = P#Pre#Neg
  type Pre = P#Succ#Neg
}
  

Еще одна вещь, / метод в Quantity должен разделять свои параметры, а не умножать их!

Комментарии:

1. Да, это исправление. Я внес два исправления в репозиторий MetaScala.