metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2013-10-11 08:39 am

Type classes, implicits и Expression Problem

Читаю про тайпклассы и имплиситы в скале:
http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Не совсем понимаю один момент. В статье мы вызываем функцию (printLabel) для объекта, конкретная реализация которой ищется компилятором в виде имплисита в скале или в реализации тайпкласса в хаскеле. Но это все, условно говоря "раннее связывание", на этапе компиляции.

А вот если мы ходим сделать универсальную функцию вроде "на входе список объектов, на выходе - список строковых представлений", причем, во-первых, объекты сами по себе ни от чего типа "Object с методом printLabel" не наследуются и интерфейсов соответствующих не реализуют (ну, скажем объект - это Anemic Data Model, тупая запись с полями и прочее POCO/POJO вообще), а printLabel реализована вот как в статье - в отдельном связанном объекте-адаптере - то как, во-первых, мы запишем тип такой функции, а во вторых - как мы ее реализуем?

В хаскеле можно было намутить чего-то с соответствующим тайпклассом и экзистенциальными типами ("список любых объектов, у которых есть тайпкласс LabelMaker"). А с трейтами и прочей скалой чего можно сделать?

На мирных языках типа жабе, C# или дельфей - придется вообще делать диспетчеризацию по типу и глобальный словарь вида "тип->функция", регистрировать в нем функции печати(в F# pretty printer вроде так и реализован), но это никаких статических гарантий правильности вообще не даст. Впрочем, в стиле "хуяк-хуяк и в продакшен" это не проблема - для всех типов, где мы не нашли функцию - выводим надпись "ИмяТипа_ОбратитесьВОбслуживающуюОрганизациюЕслиВыВидитеЭтоИЗаплатите100500ДенегЗаОбслуживание" и все, ничего не упадет, кому надо - обратятся.

[identity profile] jakobz.livejournal.com 2013-10-11 06:05 am (UTC)(link)
Паттерно-дрочерский способ решать эту проблему - визитор, но я не видел ни разу чтобы кто-нибудь в здравом уме реально эту дроч делал.

[identity profile] bydl0coder.livejournal.com 2013-10-11 06:38 am (UTC)(link)
Хуяк-хуяк!
def print(o)
  method = "print_#{o.class.name.underscore}"
  respond_to?(method) ? send(method, o) : o.class.name
end


В скале относительно недавно появились implicit classes, с ними можно так делать
class Person(val name: String)

trait Printable[T] {
  def print : String
}

object Implicits {
  implicit class PimpedPerson(p: Person) extends Printable[Person] {
    def print = p.name
  }
}

object Program {
  import Implicits._

  def printList[a <% Printable[a]](xs: List[a]) = xs.map(_.print)

  def main(args: Array[String]): Unit = {
    val ps = List(new Person("Iceberg Slim"))
    printList(books)
  }
}


Но со списком разных объектов хуй, ибо он получается List[Object], а из Object в Printable[Object] конверсии нет. Но, возможно, есть какой-нить способ извернуться.

(Anonymous) 2013-10-11 07:00 am (UTC)(link)
Ну вообще это похоже на адаптеры в Python. В Delphi мне кажется тут поможет RTTI

[identity profile] xeno-by.livejournal.com 2013-10-11 07:04 am (UTC)(link)
По условиям задачи можно ли использовать гетерогенные списки вместо обычных?

[identity profile] potan.livejournal.com 2013-10-11 07:11 am (UTC)(link)
В хаскеле "с соответствующим тайпклассом и экзистенциальными типами" не все так просто. Если написать f :: forall a . Show a => [a] -> String, то получится функция, которая принимает список элемента типа, принадлежащего Show, а не списка любой херни, для которого есть инстанс Show.
В скале придется делать аналогично и implicits помещать в структуре данных вместе со значением, которое им будет обрабатываться. Получается не столь элегантно, но и проблема, в общем то, надуманная.

[identity profile] berezovsky.livejournal.com 2013-10-11 07:50 am (UTC)(link)
.GetType().ToString() :-))

[identity profile] pcmag-ru.livejournal.com 2013-10-12 10:47 am (UTC)(link)
Сорри за офтопик, сообщения в ЖЖ не отправляются. А куда можно написать (мыло, другие контакты)?

[identity profile] jdevelop.livejournal.com 2013-10-12 11:03 pm (UTC)(link)
object ToStringers {

   type ToString = (Any) => String

   val nonExistent(x: Any) = "Does not exists"

   val stringers : Map[Class[Any], ToString] = Map (
      classOf[String] -> _.toString,
      classOf[MyPojo] -> _.asInstanceOf[MyPojo].name
   )

   def traverse(src: Seq[Any]): Seq[String] = src.map(x => stringers.get(x.getClass).getOrElse(notExistent)(x))

}