Сопоставление с образцом на вложенных типах в Scala - PullRequest
4 голосов
/ 23 апреля 2011

Я пытаюсь реализовать что-то, что фактически является перечислением в Scala.Я хотел бы сделать это, используя классы case, чтобы компилятор мог обнаруживать любые неисчерпывающие совпадения с образцами.

Это прекрасно работает в самой базовой форме, например:

sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
...
def test (x : HorizontalAlignment) = 
  x match {
    case Left => ...
    ...  
  } 

Однако это не идеально, поскольку имена объектов case могут легко конфликтовать:

sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment

sealed abstract class VerticalAlignment
case object Top extends VerticalAlignment
case object Bottom extends VerticalAlignment
case object Center extends VerticalAlignment
case object AsIs extends VerticalAlignment

// "Center" and "AsIs" clash

Очевидное решение - поместить объекты case в отдельные пространства имен:

sealed abstract class HorizontalAlignment {
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

sealed abstract class VerticalAlignment {
  case object Top extends VerticalAlignment
  case object Bottom extends VerticalAlignment
  case object Center extends VerticalAlignment
  case object AsIs extends VerticalAlignment
}

Но какссылаться на эти классы в блоке совпадений?

На них нельзя ссылаться точками в стиле Java:

def test (x : HorizontalAlignment) = 
x match {
  case HorizontalAlignment.Left => 0  //  error: not found: value HorizontalAlignment
}

Символ "#" также не работает:

def test (x : HorizontalAlignment) = 
x match {
  case HorizontalAlignment#Left => 0 // error: '=>' expected but '#' found 
}

И эта форматоже не работает:

def test (x : HorizontalAlignment) = 
x match {
  case _ : HorizontalAlignment#Left => 0  // error: type Left is not a member of Test.HorizontalAlignment
}

Это имеет смысл, так как «Левый» в данном случае является экземпляром, а не типом, и я подозреваю, что есть простой способ ссылки на тип.Самое близкое к этому достижение, которое я мог бы получить, это:

sealed abstract class HorizontalAlignment {
  case class Left extends HorizontalAlignment
  case class Right extends HorizontalAlignment
  case class Center extends HorizontalAlignment
  case class AsIs extends HorizontalAlignment

  object Left
  object Right
  object Center
  object AsIs

}

Но хотя это и приводит к хорошей компиляции блока соответствия, я не смог найти никакого способа на самом деле ссылаться на эти объекты, напримерпередать член этого «перечисления» в функцию.Это связано с тем, что HorizontalAlignment является типом, а не объектом, и поэтому невозможно ссылаться на один из вложенных объектов с помощью доступа к полю, а, с другой стороны, эти объекты не являются типами, поэтому невозможно ссылаться на них с помощьюСимвол «#».

Можно ли ссылаться на объекты, вложенные в класс, за пределами этого класса?

EDIT

До сих пор я обнаружил, что объекты пакета являются лучшим способом решения этой проблемы.

package object HorizontalAlignment  {
  sealed abstract class HorizontalAlignment
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

package object VerticalAlignment {
  sealed abstract class VerticalAlignment 
  case object Top extends VerticalAlignment 
  case object Bottom extends VerticalAlignment 
  case object Center extends VerticalAlignment 
  case object AsIs extends VerticalAlignment 
}


object Test {
  import HorizontalAlignment.HorizontalAlignment
  import VerticalAlignment.VerticalAlignment 

  def test (x : HorizontalAlignment, y : VerticalAlignment) =  {
    x match {
      case HorizontalAlignment.Left => ...
      ...
    }

    y match {
      case VerticalAlignment.Top => ...
      ...
    }
  }

  def testTest = test (HorizongalAlignment.Left, VerticalAlignment.Top)

}

Однако, вопрос выше (доступвложенных объектов в классах) до сих пор стоит.

Ответы [ 2 ]

11 голосов
/ 23 апреля 2011

Вам не нужно использовать объекты пакета, которые могут иметь дополнительную нежелательную семантику: обычные старые объекты-компаньоны так же хороши:

sealed trait HorizontalAlignment
object HorizontalAlignment {
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

scala> def test (x : HorizontalAlignment) = x match {
     |   case HorizontalAlignment.Left => "got left"
     | }

scala> test(HorizontalAlignment.Left)
res0: java.lang.String = got left

Проблема, с которой вы столкнулись, заключалась в том, что, поскольку HorizontalAlignment был абстрактным классом , не было экземпляра HorizontalAlignment для разыменования. С вашей оригинальной формулировкой в ​​пространстве имен вам нужно создать экземпляр экземпляра HorizontalAlignment, а внутренние объекты будут специфичными для этого экземпляра. Однако, поскольку HorizontalAlignment запечатан, вы не можете создать такой экземпляр в любом другом модуле компиляции, кроме того, в котором он был определен, поэтому ваши перечисленные значения фактически никогда не могут быть получены никакими средствами.

В отличие от Java, с классами не связано «статическое пространство имен»; чтобы получить эквивалент, вы должны использовать сопутствующий объект.

2 голосов
/ 24 апреля 2011

Вы уже мудро уклонились от этой структуры, но чтобы ответить на оставшийся вопрос: чтобы обратиться к элементу-значению класса, для которого у вас нет экземпляра, вам придется прибегнуть к существующим.

sealed abstract class HorizontalAlignment {
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

object Test {
  type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }

  def test(x: HorizontalAlignment): Int = x match {
    case _: LeftOb => 0
  }
}

Неудивительно (что неудивительно, если вы - я), пытаясь использовать этот тип в сопоставлении с образцом, вылетает bejeezus из компилятора. Но в принципе это способ выразить это.

Редактировать: люди, кажется, отвлечены моим указанием на крушение совпадения шаблонов. Позвольте мне проиллюстрировать в меньшей степени, что а) это единственный способ выразить рассматриваемое понятие и б) он работает.

sealed abstract class HorizontalAlignment {
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

object Test {
  type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }

  def f(x: Any) = x.isInstanceOf[LeftOb]

  def main(args: Array[String]): Unit = {
    val ha = new HorizontalAlignment { }
    println(f(ha.Left))
    println(f(ha.Right)) 
  }
}

Выход:

true
false
...