Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 27.05.11 18:52
Оценка:
Вот такой код компилируется:

module M
{
  Main() : void 
  {
      def foo[T](x : T) {}
      def bar[T](x : T) {}
      
      [foo, bar].Iter(f => f(1));
      [foo, bar].Iter(f => f(""));
  }
}


Возможно ли (хотя бы в принципе) сделать так, чтобы такой код тоже компилировался?

module M
{
  Main() : void 
  {
      def foo[T](x : T) {}
      def bar[T](x : T) {}
      
      def func_list = [foo, bar];
      func_list.Iter(f => f(1));
      func_list.Iter(f => f(""));
  }
}
Re: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.05.11 00:09
Оценка:
Здравствуйте, nikov, Вы писали:

N>Возможно ли (хотя бы в принципе) сделать так, чтобы такой код тоже компилировался?

N>
N>module M
N>{
N>  Main() : void 
N>  {
N>      def foo[T](x : T) {}
N>      def bar[T](x : T) {}
      
N>      def func_list = [foo, bar];
N>      func_list.Iter(f => f(1));
N>      func_list.Iter(f => f(""));
N>  }
N>}
N>

Разве что вот так:
      def func_list = [foo, bar] : list[object -> void];
      func_list.Iter(f => f(1));
      func_list.Iter(f => f(""));


В общем, сделать чтобы компилировался может и можно, но рантайм дотнетный такого не поймет. Это же попытка работать с не воплощенным параметром типов.

К чему этот вопрос? Что-то не хорошее задумал?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 02.06.11 01:17
Оценка: 59 (3) :)
Здравствуйте, VladD2, Вы писали:

N>>Возможно ли (хотя бы в принципе) сделать так, чтобы такой код тоже компилировался?


VD>Разве что вот так:

VD>
VD>      def func_list = [foo, bar] : list[object -> void];
VD>


Нет, идея именно в том, чтобы сохранить тип полиморфным.

VD>В общем, сделать чтобы компилировался может и можно, но рантайм дотнетный такого не поймет. Это же попытка работать с не воплощенным параметром типов.


Ведь функциональный тип в немерле — это, по сути, класс с виртуальным методом apply, правда? Почему бы не представлять функциональное значение со свободным типом-параметром в виде класса с виртуальным generic методом apply[T]?

VD>К чему этот вопрос? Что-то не хорошее задумал?


Пример на псевдокоде.

Была у нас функция сортировки списка чисел, которая вызывалась из какого-то метода:

module M
{
  Main() : void 
  {
      Foo()
  }
  
  Foo() : void
  {
      def x = [1, 2, 3];
      // ...
      Sort(x)
      // ...
  }
  
  Sort(x : list[int]) : list[int] 
  { 
    // ...
  }
}


Васе понадобилось поправить этот код, чтобы появились функции Reverse (которая переворачивает список задом-наперед) и Shuffle (которая переставляет элементы случайным образом), и чтобы функция Foo могла принимать преобразование, которое она применяет к списку x, в качестве параметра. Вася сделал бранч и написал в нём такой код:

module M
{
  Main() : void 
  {
      // ...
      Foo(Sort);
      
      // ...
      Foo(Reverse);
      
      // ...
      Foo(Shuffle);
  }
  
  Foo(transform : list[int] -> list[int]) : void
  {
      def x = [1, 2, 3];
      // ...
      transform(x)
      // ...
  }
  
  Sort(x : list[int]) : list[int] 
  { 
    // ...
  }

  Reverse(x : list[int]) : list[int] 
  { 
    // ...
  }

  Shuffle(x : list[int]) : list[int] 
  { 
    // ...
  }
}


Тем временем Петя тоже поменял этот код: ему понадобилось сортировать в методе Foo не только список чисел, но и список строк. Поэтому он переделал метод Sort в generic метод:

module M
{
  Main() : void 
  {
      Foo()
  }
  
  Foo() : void
  {
      def x = [1, 2, 3];
      // ...
      Sort(x)
      // ...

      def y = ["A", "B", "C"];
      // ...
      Sort(y)
      // ...
  }
  
  Sort[T](x : list[T]) : list[T] where T : IComparable[T]
  { 
    // ...
  }
}


Тут Вася начинает мёржить бранч со своими изменениями, и получает конфликт, который надо разрезолвить. Поговорив с Петей, он понимает почему метод Sort должен быть generic, и решает, что методы Reverse и Shuffle тоже должны быть generic. Он меняет свой код следующим образом:

using System;

module M
{
  Main() : void 
  {
      // ...
      Foo(Sort);
      
      // ...
      Foo(Reverse);
      
      // ...
      Foo(Shuffle);
  }
  
  Foo(transform : list[?] -> list[?]) : void
  {
      def x = [1, 2, 3];
      // ...
      transform(x)
      // ...

      def y = ["A", "B", "C"];
      // ...
      transform(y)
      // ...
  }
  
  Sort[T](x : list[T]) : list[T] where T : IComparable[T]
  { 
    // ...
  }
 
  Reverse[T](x : list[T]) : list[T] 
  { 
    // ...
  }

  Shuffle(x : list[T]) : list[T] 
  { 
    // ...
  }
}


Изменения почти удалось смёржить, проблема только в том, что Вася не знает, какую указать сигнатуру у метода Foo. Ему нужно абстрагироваться от передаваемой в метод Foo generic-функции, но так чтобы она не перестала быть generic.
Вася уже собрался городить вспомогательные классы для каждого из методов Sort, Reverse, Shuffle, но вспомнил, что он пишет на замечательном расширяемом языке, и хорошо разбирается в коде компилятора, поэтому он решает реализовать в языке следующую полезную фичу:

module M
{
  Main() : void 
  {
      // ...
      Foo(Sort);
      
      // ...
      Foo(Reverse); // Заметьте, что отсутствие констрейнта у метода Reverse не мешает передать его в метод Foo
      
      // ...
      Foo(Shuffle);
  }
  
  Foo(transform[T] : list[T] -> list[T] where T : IComparable[T]) : void
  {
      def x = [1, 2, 3];
      // ...
      transform(x)
      // ...

      def y = ["A", "B", "C"];
      // ...
      transform(y)
      // ...
  }
  
  Sort[T](x : list[T]) : list[T] where T : IComparable[T]
  { 
    // ...
  }
 
  Reverse[T](x : list[T]) : list[T] 
  { 
    // ...
  }

  Shuffle(x : list[T]) : list[T] 
  { 
    // ...
  }
}


Изменения удалось изящно смёржить, а любимый Васин и Петин язык стал ещё мощнее!
Re[3]: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.06.11 14:02
Оценка:
Здравствуйте, nikov, Вы писали:

N>Изменения почти удалось смёржить, проблема только в том, что Вася не знает, какую указать сигнатуру у метода Foo. Ему нужно абстрагироваться от передаваемой в метод Foo generic-функции, но так чтобы она не перестала быть generic.

N>Вася уже собрался городить вспомогательные классы для каждого из методов Sort, Reverse, Shuffle, но вспомнил, что он пишет на замечательном расширяемом языке, и хорошо разбирается в коде компилятора, поэтому он решает реализовать в языке следующую полезную фичу:

Можно было и без прелюдий . Мы тут люди не дубовые.

N>
N>module M
N>{
N>  Main() : void 
N>  {
N>      // ...
N>      Foo(Sort);
      
N>      // ...
N>      Foo(Reverse); // Заметьте, что отсутствие констрейнта у метода Reverse не мешает передать его в метод Foo
      
N>      // ...
N>      Foo(Shuffle);
N>  }
  
N>  Foo(transform[T] : list[T] -> list[T] where T : IComparable[T]) : void
N>  {
N>      def x = [1, 2, 3];
N>      // ...
N>      transform(x)
N>      // ...

N>      def y = ["A", "B", "C"];
N>      // ...
N>      transform(y)
N>      // ...
N>  }
    ...
N>}
N>


N>Изменения удалось изящно смёржить, а любимый Васин и Петин язык стал ещё мощнее!


То есть ты это дело предлагаешь переписывать в:
abstract class F
{
  public abstract apply[T](_ : T) : T;
}

module Program
{
  Foo(transform : F) : void
  {
      def x = [1, 2, 3];
      // ...
      _ = transform.apply(x);
      // ...

      def y = ["A", "B", "C"];
      // ...
      _ = transform.apply(y);
      // ...
  }
  ...


Можно конечно. Вопрос только в том, насколько это часто будет востребовано?

Где-нибудь подобное реализовано? Если, да то какова практика применения?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Список generic функций
От: Аноним  
Дата: 02.06.11 18:28
Оценка:
Здравствуйте, VladD2, Вы писали:
вот это я понимаю под передaчей типа в функцию. Для скриптовых языков это норма.
Re[4]: Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 02.06.11 19:42
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Где-нибудь подобное реализовано?

В Haskell такое есть, называется rank-2 types. Там есть и дальнейшее обобщение этой фичи — rank-N types, позволяющее передавать в качестве параметра полиморфные функции наподобие Foo из моего примера. В Clean есть rank-2 types (rank-N планируются). Не знаю ни одного ООП-языка, где бы это было реализовано (в F# и Scala точно нет).

VD>Если, да то какова практика применения?

Вот это я не знаю наверняка, но мне кажется, что эта фича очень естественная и была бы востребована. Можно поспрашивать у хаскелистов.
Re[5]: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 03.06.11 13:44
Оценка:
Здравствуйте, nikov, Вы писали:

VD>>Если, да то какова практика применения?

N>Вот это я не знаю наверняка, но мне кажется, что эта фича очень естественная и была бы востребована.

Откровенно говоря берут меня смутные сомнения по поводу востребованности данной фичи. Одесски ведь явно попытался бы это дело прикруить в Скалу, если он было бы реально надо.

N>Можно поспрашивать у хаскелистов.


Да, надо попробовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 09.06.11 02:31
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Откровенно говоря берут меня смутные сомнения по поводу востребованности данной фичи. Одесски ведь явно попытался бы это дело прикруить в Скалу, если он было бы реально надо.


Поигрался немножно со Скалой, и у меня появилась идея, как можно записывать rank-N типы с помощью структурных типов. Но там есть проблемы с выводом типов, и оно пока не работает. Я попробую продвинуть эту идею разработчикам. Если будут планы реализовать в Nemerle структурные типы, то я могу поделиться мыслями об их дизайне.

Идея основана на том, что функциональное значение является объектом с методом apply. Базовый синтаксис (наверное, можно подсахарить макросами) я представляю себе примерно таким (Scala-псевдокод):

object A {
  def foo[T](x : List[T]) : List[T] = x
  def bar(f : { def apply[T](x : List[T]) : List[T] }, x : List[String], y : List[Integer]) { f(x); f(y)}
  def qux(x : List[String], y : List[Integer]) { bar(foo(_), x, y) }
}
Re[7]: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.06.11 14:45
Оценка:
Здравствуйте, nikov, Вы писали:

N>Если будут планы реализовать в Nemerle структурные типы, то я могу поделиться мыслями об их дизайне.


Желание есть. Я делал пару подходов к дизайну, но ничего путного не вышло. Получается кучча оберточного кода который явно не пройдет даром (в смысле производительности). Так что если есть идеи, то с удовольствием послушаем.

N>Идея основана на том, что функциональное значение является объектом с методом apply. Базовый синтаксис (наверное, можно подсахарить макросами) я представляю себе примерно таким (Scala-псевдокод):


N>
N>object A {
N>  def foo[T](x : List[T]) : List[T] = x
N>  def bar(f : { def apply[T](x : List[T]) : List[T] }, x : List[String], y : List[Integer]) { f(x); f(y)}
N>  def qux(x : List[String], y : List[Integer]) { bar(foo(_), x, y) }
N>}
N>


object — это синглтон?

Мне больше нравится такой синтаксис:
object A
{
  def foo[T](x : List[T]) : List[T] = x
  def bar(f[T] : List[T] -> List[T], x : List[String], y : List[Integer]) { f(x); f(y)}
  def qux(x : List[String], y : List[Integer]) { bar(foo(_), x, y) }
}


Причем тут структурные типы тоже не очень понятно. "f" ведь просто функциональное значение. Как я понимаю в Скале синтаксис вроде f : { def apply[T](x : List[T]) : List[T] } используется для того чтобы в такой параметр можно было передать тип обладающий методом с указанной сигнатурой.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 09.06.11 18:09
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>object — это синглтон?


Да. Наподобие static type, но first-class (может передаваться в качестве значения, экстендить trait-ы и т.д.)

VD>Мне больше нравится такой синтаксис:

VD>
VD>  def bar(f[T] : List[T] -> List[T], x : List[String], y : List[Integer]) { f(x); f(y)}
VD>


Да, этот синтаксис короче и симпатичнее. Синтаксис, основанный на структурных типах, более общий (позволяет записать не только rank-2, но и произвольные rank-N) и, так сказать, синтаксически "бесплатный" (не требует никаких новых конструкций — по крайней мере, для Scala, где уже есть структурные типы). Краткий синтаксис можно сделать синтаксическим сахаром для частного случая, когда используется rank-2 тип.

VD>Причем тут структурные типы тоже не очень понятно. "f" ведь просто функциональное значение. Как я понимаю в Скале синтаксис вроде f : { def apply[T](x : List[T]) : List[T] } используется для того чтобы в такой параметр можно было передать тип обладающий методом с указанной сигнатурой.


В Скале используется "тотальный" ООП подход: каждое значение — это объект, каждая операция — это вызов метода. Если f — значение функционального типа, то оно также является объектом, и операция f(x) — это всего лишь сокращённая запись вызова метода f.apply(x). Причем это — чистый синтаксический сахар: вызов метода apply у любого объекта obj (не только у значения функционального типа) может быть записан как obj(x). В свете этого, функциональные типы можно понимать просто как частный случай структурных типов.
Re[9]: Список generic функций
От: nikov США http://www.linkedin.com/in/nikov
Дата: 09.06.11 18:27
Оценка:
Здравствуйте, nikov, Вы писали:

N> В свете этого, функциональные типы можно понимать просто как частный случай структурных типов.


Например, в Скала такой код успешно компилируется:

object A {
  def apply(x : String) : String = x
  def f : String => String = A(_) // к сожалению, частичное применение надо указывать явно
  def s : { def apply(x : String) : String } = f
}


Будь моя воля, я бы сделал функциональный тип просто синтаксическим сахаром для структурного с методом apply, чтобы можно было написать так:

object A {
  def apply(x : String) : String = x
  def s : { def apply(x : String) : String } = A
  def f : String => String = A
  def g : String => String = s
}


(надеюсь, никто не обидится за маленькое злоупотребление тегами )
Re[7]: Список generic функций
От: Ziaw Россия  
Дата: 10.06.11 05:44
Оценка:
Здравствуйте, nikov, Вы писали:

N>Если будут планы реализовать в Nemerle структурные типы, то я могу поделиться мыслями об их дизайне.


Планы есть, но как реализовать их без тяжелых рантайм проверок непонятно.

f(x : object) : void
{
  def y = x :> classwith { apply() : void; };
  y.apply();
}


Единственная идея у меня — замена структурного типа на интерфейс и генерация классов проксей для каждого встреченного типа в рантайме. С выносом в компайлтайм проксей для тех типов которые мы можем узнать при анализе кода.
Re[8]: Список generic функций
От: hardcase Пират http://nemerle.org
Дата: 10.06.11 08:40
Оценка: +1
Здравствуйте, Ziaw, Вы писали:

Z>Единственная идея у меня — замена структурного типа на интерфейс и генерация классов проксей для каждого встреченного типа в рантайме. С выносом в компайлтайм проксей для тех типов которые мы можем узнать при анализе кода.


Тут встают проблемы ссылочной эквивалентности. Ссылка на прокси не будет равна ссылке на объект.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[9]: Список generic функций
От: Ziaw Россия  
Дата: 10.06.11 11:23
Оценка:
Здравствуйте, hardcase, Вы писали:

Z>>Единственная идея у меня — замена структурного типа на интерфейс и генерация классов проксей для каждого встреченного типа в рантайме. С выносом в компайлтайм проксей для тех типов которые мы можем узнать при анализе кода.


H>Тут встают проблемы ссылочной эквивалентности. Ссылка на прокси не будет равна ссылке на объект.


Можно проксировать методы.
Re[10]: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.06.11 12:55
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>>>Единственная идея у меня — замена структурного типа на интерфейс и генерация классов проксей для каждого встреченного типа в рантайме. С выносом в компайлтайм проксей для тех типов которые мы можем узнать при анализе кода.


H>>Тут встают проблемы ссылочной эквивалентности. Ссылка на прокси не будет равна ссылке на объект.


Z>Можно проксировать методы.


Будет еще медленнее. Возможно выходом будет попытаться попробовать воспользоваться структурной эквивалентностью 4-го дотнета. Но она доступна только для интерфейсов и структур.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Список generic функций
От: Ziaw Россия  
Дата: 10.06.11 16:33
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Будет еще медленнее. Возможно выходом будет попытаться попробовать воспользоваться структурной эквивалентностью 4-го дотнета. Но она доступна только для интерфейсов и структур.


Чем медленнее? Дополнительный метод который вызывает нужный нам. С прокси объектом ровно тот же сценарий.
Re[12]: Список generic функций
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.06.11 17:11
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Чем медленнее? Дополнительный метод который вызывает нужный нам. С прокси объектом ровно тот же сценарий.


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

Плюс будут проблемы уровнями доступа.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.