trait оператор
От: hardcase Пират http://nemerle.org
Дата: 04.12.10 10:39
Оценка:
Навеяно идеей "типажей", traits, из Scala.

class Bar
{
  public F() : void {}
}

interface IFoo
{
  F() : void;
}


def x = Bar();
def t = x ::> IFoo;

Оператор ::> изготавливает обертку, реализующую интерфейс IFoo, перенаправляя вызовы соответствующим методом переменной x типа Bar.
Смысл в том, что тип Bar не реализует интерфейс IFoo, но его сигнатура совместима с этим интерфейсом.
/* иЗвиНите зА неРовнЫй поЧерК */
Re: trait оператор
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 04.12.10 12:50
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Оператор ::> изготавливает обертку, реализующую интерфейс IFoo, перенаправляя вызовы соответствующим методом переменной x типа Bar.

H>Смысл в том, что тип Bar не реализует интерфейс IFoo, но его сигнатура совместима с этим интерфейсом.

Выглядит интересно. Для таких трейтов есть какие-то типичные применения?
Ce n'est que pour vous dire ce que je vous dis.
Re[2]: trait оператор
От: hardcase Пират http://nemerle.org
Дата: 04.12.10 13:45
Оценка: 29 (3)
Здравствуйте, Don Reba, Вы писали:

DR>Выглядит интересно. Для таких трейтов есть какие-то типичные применения?


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

В Nemerle подобное неявное приведение сейчас невозможно — для этого необходимо перекраивать алгоритм выбора перегрузок — но зато можно сделать оператор явного приведения (::>).

Конечная цель трайтов — понизить связность кода. Например, если наш класс использует лишь часть функционала некоторого другого класса то мы можем описать этот ограниченный интерфейс (трайт) и работать в нашем классе с ним.


class SomeHugeApiClass
{
   ...
   
   public Foo() : void {}
   
   public Bar : string { get; set; }

   ...
}


// это "trait"
interface TPartialApiClass
{
  Foo() : void;
  Bar : stirng { get; set; }
}

[Record]
class MySimpleClass
{
  api : TPartialApiClass;

  public UseApi() : void
  {
    api.Bar = "hi!";
    api.Foo();
  }
}

def api : SomeHugeApiClass = ...;

def x = MySimpleClass(api ::> TPartialApiClass);
x.UseApi();


Вообще можно прикинуть над "утиной" типизацией в генериках. Ограничения в генериках описываем интерфейсами, а передачу фактических параметров осуществляем с помощью этого оператора.
/* иЗвиНите зА неРовнЫй поЧерК */
Re: trait оператор
От: hi_octane Беларусь  
Дата: 04.12.10 19:08
Оценка:
H>Навеяно идеей "типажей", traits, из Scala.
def x = Bar();
def t = x ::> IFoo;


//и тут внезапно
ReferenceEquals(x, t) == подозреваю что false


Интересно, и даже представляю где и как применить. Но нужно подумать как потом обеспечить работу Equals, ReferenceEquals и приведение назад к X а также к интерфейсам которые X поддерживает. Т.е. если пытаться сделать качественно то может потребоваться очень много работы.
Re[2]: trait оператор
От: dotneter  
Дата: 04.12.10 19:56
Оценка:
Здравствуйте, hi_octane, Вы писали:

А при использовании патернов типа адаптер вы тоже требуете ReferenceEquals?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re: trait оператор
От: dotneter  
Дата: 04.12.10 19:56
Оценка: +1
Здравствуйте, hardcase, Вы писали:

А в чем отличие от DynamicProxy?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[2]: trait оператор
От: Аноним  
Дата: 04.12.10 20:37
Оценка:
Здравствуйте, dotneter, Вы писали:

D>Здравствуйте, hardcase, Вы писали:


D>А в чем отличие от DynamicProxy?


Тем что этот оператор работает на этапе компиляции и если сигнатура интерфейса (трайта) не совпала с сигнатурой объекта, генерируется ошибка компиляции.
Re[2]: trait оператор
От: Аноним  
Дата: 04.12.10 20:48
Оценка:
Здравствуйте, hi_octane, Вы писали:

H>>Навеяно идеей "типажей", traits, из Scala.

_>
_>def x = Bar();
_>def t = x ::> IFoo;

_>
_>//и тут внезапно
_>ReferenceEquals(x, t) == подозреваю что false
_>
_>


_>Интересно, и даже представляю где и как применить.


Колись. Меня как-раз интересует, нужна ли кому-то такая штука.

_>Но нужно подумать как потом обеспечить работу Equals, ReferenceEquals и приведение назад к X а также к интерфейсам которые X поддерживает. Т.е. если пытаться сделать качественно то может потребоваться очень много работы.


С ReferenceEquals я думаю никак. Equals и GetHashcode можно (и скорее всего нужно) делегировать.
Re[2]: trait оператор
От: hardcase Пират http://nemerle.org
Дата: 04.12.10 21:56
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>... приведение назад к X а также к интерфейсам которые X поддерживает.


Создавая экземпляр trait можно реализовывать специальный интерфейс (по аналогии с IAnonymous) — ITrait — приводя к которому нашу ссылку можно получать "обернутый" объект:

// специальный интерфейс
interface ITrait
{
  WrappedObject : object { get; }
}


...


match(t)
{
  // мы получили обертку, сгенерированную оператором ::> 
  | ITrait where (WrappedObject as orig)

  // что-то иное - видимо пользовательская реализация интерфейса
  | orig =>
    //работаем с оригинальной ссылкой 
}
/* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: trait оператор
От: hi_octane Беларусь  
Дата: 04.12.10 22:03
Оценка:
А>Колись. Меня как-раз интересует, нужна ли кому-то такая штука.
А как только работаешь с _похожими_ данными из разных источников — так сразу и вспоминаешь про множественное наследование и трэйтсы, и ducktyping. Например мы получаем данных от двух серверов одной конторы, один в США, другой где-то в Азии. Веб-сервисы равны до степени смешения, но таки разные из-за региональных фишек, формата валют и т.п. мелочи. В той части которая абсолютно одинакова для обоих — мы бы применили что-то такое, но увы дотумкать смогли только до аттрибута который навешивается на класс, и генерирует реализацию общего интерфейса к которому кастим. Кроме того генератор кода для этого веб-сервиса был на C#, пришлось перевести на N, хоть и не планировали, короче будь трэйты в такой форме заюзали бы со щастем.

_>>Но нужно подумать как потом обеспечить работу Equals, ReferenceEquals и приведение назад к X а также к интерфейсам которые X поддерживает. Т.е. если пытаться сделать качественно то может потребоваться очень много работы.


А>С ReferenceEquals я думаю никак. Equals и GetHashcode можно (и скорее всего нужно) делегировать.

Допустим Equals и GetHashcode да, а там рядом ещё IComparable. И тогда как? Преобразование к интерфейсу реально ли делегировать? Или придётся использовать что-то вроде интерфейса ITraitedObject для кастинга к реальным типам потом? C#4-ve dynamic'у как-то удаётся кастинг назад к интерфейсам и реальному объекту, но какая там магия я пока не вникал. Может кто-то уже знает и может подзсказать...

Может можно переопределить как-то преобразование назад в object или в конкретный тип. Но не уверен что это обойдётся без хардкода в компилятор (а может обойдётся, тут экспериментировать надо проверяя нюансы работы implicit/explicit преобразований). Если хардкод потребуется — то это походу слишком большая цена для такой фичи, так что можно просто задокументировать что генерируется обёртка и для фокусов надо использовать Underlying Type. Но тогда этот underlying type должен быть либо в интерфейсе прописан, либо должен быть какой-то способ его получить. Самое лучшее до чего сходу дотумкал, это что повторное преобразование t ::> Bar должно давать реальный бар, но не факт что это самый лучший вариант, нужно что-бы ещё лбы к обсуждению подключились. Кроме того как-то надо обдумать повторный trait, т.е. если (((x ::> IFoo) ::> IAlice) ::> IBob) должно таки работать, что потребует для оператора ::> умения отличать уже затрэйченый объект от обычного.



Кстати, видел и другие идеи на эту тему для C#, но тоже интересные:

dynamic можно было сделать дженериком, типа dynamic<IDisposable>, тогда в рамках заявленного интерфейса автокомплит и статическая типизация работали бы и для динамиков, несмотря на полную динамичность генерируемого кода. Но в C# как всегда слегка облажались, в Nemerle можно сделать классной фичей.

Сейчас C#-овский dynamic не кастится к интерфейсам совсем, если тип под ним интерфейс не поддерживает, можно было бы сделать duck-typing интерфейсов в dynamic, но тоже не сделали.
Re[3]: trait оператор
От: hi_octane Беларусь  
Дата: 04.12.10 22:12
Оценка:
H>Создавая экземпляр trait можно реализовывать специальный интерфейс (по аналогии с IAnonymous) — ITrait — приводя к которому нашу ссылку можно получать "обернутый" объект:
Думал об этом, но ощущения законченности решения не возникает. Не должен человек который получил интерфейс, запариваться над тем что там ему под этим интерфейсом передали. Т.е. кастинг должен прозрачно работать. Например для C#4 dynamic кастинг в dynamic implicit, назад explicit, ReferenceEquals работает, и никаких доп интерфейсов. Надо к этому стремиться.
Re[4]: trait оператор
От: hardcase Пират http://nemerle.org
Дата: 04.12.10 22:27
Оценка:
Здравствуйте, hi_octane, Вы писали:

H>>Создавая экземпляр trait можно реализовывать специальный интерфейс (по аналогии с IAnonymous) — ITrait — приводя к которому нашу ссылку можно получать "обернутый" объект:

_>Например для C#4 dynamic кастинг в dynamic implicit, назад explicit, ReferenceEquals работает, и никаких доп интерфейсов.

dynamic в .NET 4.0 реализуется с помощью класса System.Object. Связывание членов происходит с помощью рантайм привязки, которая реализует логику выбора перегрузок компилятора C#, например в связи с этим есть мощные спец-эффекты — к свойствам анонимного класса нельзя обратиться из другой сборки.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[4]: trait оператор
От: hardcase Пират http://nemerle.org
Дата: 04.12.10 22:47
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>>>Но нужно подумать как потом обеспечить работу Equals, ReferenceEquals и приведение назад к X а также к интерфейсам которые X поддерживает. Т.е. если пытаться сделать качественно то может потребоваться очень много работы.


А>>С ReferenceEquals я думаю никак. Equals и GetHashcode можно (и скорее всего нужно) делегировать.

_>Допустим Equals и GetHashcode да, а там рядом ещё IComparable. И тогда как?

При объявлении интерфейса-трайта добавлять IComparable:
interface TMyTrait : IComparable[X] { }


Преобразование к интерфейсу реально ли делегировать? Или придётся использовать что-то вроде интерфейса ITraitedObject для кастинга к реальным типам потом?

Это самый неприятный момент — обратное преобразование. Так как я предполагаю генерировать прокси-объект, штатная операция приведения типа без компиляторной магии работать не будет. Вообще я все чаще начинаю задумываться о необходимости поддержки в компиляторе специальных типов-оберток, через которые будет возможно более прозрачно интегрировать в систему типов такие вещи как Nullable[T], Lazy[T], или вот трэйты.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[5]: trait оператор
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.12.10 07:43
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Это самый неприятный момент — обратное преобразование. Так как я предполагаю генерировать прокси-объект, штатная операция приведения типа без компиляторной магии работать не будет.


С ней тоже не будет. Операторы приведения типов и даже неявные — это не более чем синтаксический сахар. Для подтипов он вообще не применим (дотнет не позволяет). Так что преобразование от твоей обертки к исходному типу возможна, а обратное невозможно. Единственное решение — это наследование от исходного типа, но при этом приведение на лету будет невозможно или потребует полного копирования данных, что не всегда приемлемо.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: trait оператор
От: Alexey Romanov  
Дата: 05.12.10 07:51
Оценка: 20 (1) +1
Здравствуйте, hardcase, Вы писали:

H>Здравствуйте, Don Reba, Вы писали:


DR>>Выглядит интересно. Для таких трейтов есть какие-то типичные применения?


H>В Scala трайты занимают ту же нишу, что и интерфейсы в Java. Но они мощнее, насколько я понял для них работают механизмы структурной типизации: если сигнатура некоторого класса совместима с сигнатурой трайта, то экземпляр этого класса можно неявно привести к этому трайту.


Неправильно поняли Traits отдельно, структурная типизация отдельно. Пруф:

object Test {
    trait Foo { def foo: Int }
    
    class NotFoo { def foo = 1 }
    
    def main(args: Array[String]) = {    
        val bar1 = (new NotFoo).asInstanceOf[{def foo: Int}]
        println("Structural: " + bar1.foo.toString)
        val bar2 = (new NotFoo).asInstanceOf[Foo]
        println("Trait: " + bar2.foo.toString)
        0
    }
}

F:\MyProgramming\raw>scalac Main.scala

F:\MyProgramming\raw>scala Test
Structural: 1
java.lang.ClassCastException: Test$NotFoo cannot be cast to Test$Foo
        at Test$.main(Main.scala:9)
        at Test.main(Main.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:81)
        at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
        at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:86)

        at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:81)
        at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:86)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:83)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Re: trait оператор
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.12.10 08:05
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Навеяно идеей "типажей", traits, из Scala.

H>...Оператор ::> изготавливает обертку, реализующую интерфейс IFoo,

Разочарую тебя. То что описываешь ты никакого отношения к traits-ам Склы не имеет. Ты описал классическую обертку.

Так же разочарую на счет возможности реализации прозрачного приведения типов туда и обратно. Насколько мне известно это под дотнетом невозможно. В дотнете есть только понятие подтипа (реализуемое через наследование и реализацию интерфейсов). Операторы приведения типов это не более чем синтаксическое соглашения для вызова некоторых методов (которые по соглашению должны эмулировать приведение типов). Фича эта работает на стадии компиляции (т.е. совсем не динамическая). Она мало чем отличается от обычного вызова методов.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: trait оператор
От: Denom Украина  
Дата: 06.12.10 13:33
Оценка: +1
Здравствуйте, hardcase, Вы писали:

H>Навеяно идеей "типажей", traits, из Scala.


Я думал это утиная типизация... Но, тоже прикольно...

А traits описаны здесь
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.