metaclass: (Default)
[personal profile] metaclass
Читаю про тайпклассы и имплиситы в скале:
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ДенегЗаОбслуживание" и все, ничего не упадет, кому надо - обратятся.

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

Date: 2013-10-11 06:16 am (UTC)
From: [identity profile] bydl0coder.livejournal.com
Double-dispatch все делали.

Date: 2013-10-11 06:38 am (UTC)
From: [identity profile] bydl0coder.livejournal.com
Хуяк-хуяк!
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] конверсии нет. Но, возможно, есть какой-нить способ извернуться.

Date: 2013-10-11 06:59 am (UTC)
From: [identity profile] xeno-by.livejournal.com
А в чем в данном случае преимущество имлисит конвершнов через вью баунды (T <% Something) перед обычными тайп классами через контекст баунды (T: Something)?

Date: 2013-10-11 07:18 am (UTC)
From: [identity profile] bydl0coder.livejournal.com
Да хз, я в типах не секу. Мне больше подход "хуяк-хуяк" нравится.

Date: 2013-10-11 07:33 am (UTC)
From: [identity profile] xeno-by.livejournal.com
Если что, здесь преимущества нет. Все может быть реализовано на тайпклассах:
trait Printable[T] {
  def print(x: T): String
}

class Person(val name: String)
object Person {
  // если использовать правильные макросы, то это определение вообще не нужно
  implicit val personIsPrintable = new Printable[Person] {
    def print(p: Person) = p.name
  }
}

object Test extends App {
  def printList[T: Printable](xs: List[T]) = xs.map(implicitly[Printable[T]].print(_))
  val books = List(new Person("Iceberg Slim"))
  printList(books)
}
Здесь есть два момента, которые я бы хотел прокомментировать:
1) Компаньон соответствующего типа включается в имплисит скоуп этого типа, поэтому имплиситы, объявленные в компаньоне, автоматически находятся компилятором без импортов (implicits without import tax).
2) В последнее время (пару лет как минимум) в коммьюнити выработалось довольно стремное отношение к implicit conversions / implicit classes. Доводы обычные - если злоупотреблять манкипатчингом, то потом тяжело разобраться какие методы откуда приходят. Поэтому народ обычно предпочитает альтернативные решения.

Date: 2013-10-11 07:01 am (UTC)
From: [identity profile] xeno-by.livejournal.com
Также, функционал, соответствующий имплисит классам (имплисит конвершны), был в Скале уже довольно долгое время. Имплисит классы это просто сахар поверх конвершнов.

Date: 2013-10-11 08:26 am (UTC)
From: [identity profile] metaclass.livejournal.com
Вот да, со списком как раз непонятно, можно ли статик гарантии сделать.

Date: 2013-10-11 08:31 am (UTC)
From: [identity profile] xeno-by.livejournal.com
В Скале сомневаюсь, если это обычный List. В Дотти думаю, что да.

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

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

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

Date: 2013-10-11 07:49 am (UTC)
From: [identity profile] jakobz.livejournal.com
Мне проще было так понять: эти все экзистенциальные типы работают через передачу хаскельного аналога vtable в рантайме. В твоем случае ты один "vtable" принимаешь на вход функции, т.е. вот это вот Show a => - это будет откомпилено в доп-параметр с лукапом функций. В этом примере тип всех элементов списка будет одинаковым.

{-# LANGUAGE ExistentialQuantification #-}

module Main where

--Чтобы тип элементов списка сделать разным - надо тип и vtable сначала слепить вместе:

data Showable = forall a . Show a => Showable a

-- Потом в список напихивать инстансы именно этой обертки (в этот момент мы как раз берем этот vtable и слепляем с инстансом):

myList = [Showable 1, Showable "test"]

-- А потом уже писать функции типа [Showable] -> String:

test :: [Showable] -> String
test = concatMap (\(Showable item) -> show item)

main = print $ test myList

Date: 2013-10-11 07:51 am (UTC)
From: [identity profile] xeno-by.livejournal.com
А можно как-нибудь обойтись без модификации myList? Хотелось бы чтобы можно было писать [1, "test"], если такое возможно.

Date: 2013-10-11 08:10 am (UTC)
From: [identity profile] jakobz.livejournal.com
Не, так вроде не выйдет. Мне тоже так сначала хотелось и казалось что этот подход с обертками - дюже кривой. Но на практике оно наоборот кажется очень прагматичным решением.

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

Во-вторых обертка обычно не такая прям и простая. И вообще эта гибкость с тем куда и зачем пихать vtable - она весьма в тему.

Date: 2013-10-11 08:11 am (UTC)
From: [identity profile] xeno-by.livejournal.com
Спасибо!

Date: 2013-10-11 08:13 am (UTC)
From: [identity profile] potan.livejournal.com
Можно. Но не на Haskell.
На питончике - запросто.

Date: 2013-10-11 07:55 am (UTC)
From: [identity profile] nealar.livejournal.com
Да, надо a заворачивать в экзистенциал, а потом уже сувать в список.

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

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

Date: 2013-10-12 11:03 pm (UTC)
From: [identity profile] jdevelop.livejournal.com
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))

}

Profile

metaclass: (Default)
metaclass

April 2017

S M T W T F S
      1
2345678
9101112 131415
16171819202122
23242526272829
30      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 27th, 2025 12:10 pm
Powered by Dreamwidth Studios