[F#] Instance methods
От: Пельмешко Россия blog
Дата: 11.09.09 17:04
Оценка:
Собственно, вопрос:
Есть ли в языке возможность использовать instance-метод в качестве функционального типа?
Ведь можно их рассматривать как статические методы с первым аргументом — this. То есть вместо:
"hello"
|> (fun s -> s.Length)
делать что-то типа:
"hello"
|> string.Length
или типа этого:
"hello"
|> _.Length

Либо я окончательно не понимаю синтаксис, либо просто ищу того, чего нету
Если фича отсутствует, то может подскажет кто причины, буду рад просветиться...
Re: [F#] Instance methods
От: VoidEx  
Дата: 11.09.09 21:12
Оценка: :)
Здравствуйте, Пельмешко, Вы писали:

Я, видимо, вопроса не понял.
ghci> let x |> f = f x
ghci> "hello" |> length
5
Re[2]: [F#] Instance methods
От: VoidEx  
Дата: 11.09.09 21:13
Оценка:
Здравствуйте, VoidEx, Вы писали:

Блин, пора спать. Куча тем с тегом haskell, я и внимания не обратил
Re: [F#] Instance methods
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.09.09 09:22
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Собственно, вопрос:

П>Есть ли в языке возможность использовать instance-метод в качестве функционального типа?
П>Ведь можно их рассматривать как статические методы с первым аргументом — this. То есть вместо:
П>
П>"hello"
П>|> (fun s -> s.Length)
П>
делать что-то типа:

П>
П>"hello"
П>|> string.Length
П>
или типа этого:

П>
П>"hello"
П>|> _.Length
П>

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

Фича отсуствует по причине невозможности подсовывания this в делегат (функциональный тип .NET).
Re[2]: [F#] Instance methods
От: Mr.Cat  
Дата: 12.09.09 09:47
Оценка:
Здравствуйте, VoidEx, Вы писали:
ghci>> "hello" |> length
Но ты недалек от истины. Для некоторых встроенных типов есть модули с наборами функций, примерно повторяющими набор функций-членов.
В данном случае, есть String.length.
Re[2]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 13:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Фича отсуствует по причине невозможности подсовывания this в делегат (функциональный тип .NET).


Не назвал бы его нормальным функциональным типом ввиду мультикастовости, к тому же в F# делегаты не используются, используется свой функциональны тип, FastFunc<T...>

Компилятор C# запрещает присваивать instance-метод делегату с сигнатурой (this + сигнатура метода), однако "подсовывать this" в CLR вполне себе возможно:
class Foo
{
  readonly int n;
  public Foo(int n)     { this.n = n; }
  public int Bar(int x) { return n + x; }
}

class Program
{
  static void Main()
  {
    var method = typeof(Foo).GetMethod("Bar");
    var type   = typeof(Func<Foo, int, int>);

    var func = (Func<Foo, int, int>)
      Delegate.CreateDelegate(type, method);

    var foo = new Foo(123);
    int val = func(foo, 321);
    //  val = 444
  }
}
Аналогично можно делать карринг первого аргумента, однако есть ограничение — работает это только для reference-типов в качестве первого аргумента...

Почему бы не разрешить подобное на уровне компилятора в F# для reference-типов?
Тем более статические и экземплярные методы вроде как не могут иметь одинаковые имена, поэтому запись "string.Length" не вносила бы никаких неясностей...
Re[3]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 13:17
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

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

ghci>>> "hello" |> length
MC>Но ты недалек от истины. Для некоторых встроенных типов есть модули с наборами функций, примерно повторяющими набор функций-членов.
MC>В данном случае, есть String.length.

Ага, только я ума не приложу почему должен за собой тащить FSharp.PowerPack.dll ради такой фундаментальной функции модуля String как length...
Re[3]: [F#] Instance methods
От: Mr.Cat  
Дата: 12.09.09 13:41
Оценка: +1 :)
Здравствуйте, Пельмешко, Вы писали:
П>Почему бы не разрешить подобное на уровне компилятора в F# для reference-типов?
Т.е. встроить в язвк шорткат для fun s -> s.Method?
Это как раз работа для простого макроса. Как там в F# с метапрограммированием?
Re[3]: [F#] Instance methods
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.09.09 13:42
Оценка:
Здравствуйте, Пельмешко, Вы писали:

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


G>>Фича отсуствует по причине невозможности подсовывания this в делегат (функциональный тип .NET).


П>Не назвал бы его нормальным функциональным типом ввиду мультикастовости, к тому же в F# делегаты не используются, используется свой функциональны тип, FastFunc<T...>

Он оборачивает обычный делегат.

П>Компилятор C# запрещает присваивать instance-метод делегату с сигнатурой (this + сигнатура метода), однако "подсовывать this" в CLR вполне себе возможно:

П>
П>class Foo
П>{
П>  readonly int n;
П>  public Foo(int n)     { this.n = n; }
П>  public int Bar(int x) { return n + x; }
П>}

П>class Program
П>{
П>  static void Main()
П>  {
П>    var method = typeof(Foo).GetMethod("Bar");
П>    var type   = typeof(Func<Foo, int, int>);

П>    var func = (Func<Foo, int, int>)
П>      Delegate.CreateDelegate(type, method);

П>    var foo = new Foo(123);
П>    int val = func(foo, 321);
П>    //  val = 444
П>  }
П>}
П>
Аналогично можно делать карринг первого аргумента, однако есть ограничение — работает это только для reference-типов в качестве первого аргумента...

Через рефлексию вообще много чего сделать можно.

П>Почему бы не разрешить подобное на уровне компилятора в F# для reference-типов?

тогда бы все работало оооооочень медленно.
Re[4]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 14:21
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Пельмешко, Вы писали:


П>>Не назвал бы его нормальным функциональным типом ввиду мультикастовости, к тому же в F# делегаты не используются, используется свой функциональны тип, FastFunc<T...>

G>Он оборачивает обычный делегат.

Ничего подобного, откуда такое убеждение? Может стоит глянуть в реализацию перед тем как писать?
public abstract class FastFunc<T, U>
{
    public abstract override U Invoke(T);

Используется точно такой же приём, как в Nemerle: все функциональные типы — это методы классов-наследников FastFunc<T...>, вызов функционального типа == виртуальный вызов, а оный быстрее чем вызов делегатов в .NET. Нельзя было закладывать в основу языка делегаты ибо они в .NET имеют немного другое назначение, в следствии чего их вызов имеет некоторый оверхед

G>Через рефлексию вообще много чего сделать можно.


Рефлексия тут не при чём, я показал, что средствами CLR сценарий осуществим.

Компилятор C#, создавая делегат вызывает метод:
  IL_000e:  newobj     instance void class [System.Core]System.Func`2<int32,int32>::.ctor(object, native int)
Почему туда не передавать адрес instance-метода, не передавая object, раз рантайм поддерживает?
C# просто запрещает такое поведение и не рассматривает методы экземпляра как статические методы с сигнатурой (this + сигнатура метода), которые можно присвоить соответствующим делегатам, да и синтаксиса нету.

П>>Почему бы не разрешить подобное на уровне компилятора в F# для reference-типов?

G>тогда бы все работало оооооочень медленно.

Всё бы работало безо всякой рефлексии, цена создания делегата была бы точно такая же как цена создания обычного делегата, указывающего на статический метод.
Re[4]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 14:29
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Здравствуйте, Пельмешко, Вы писали:

П>>Почему бы не разрешить подобное на уровне компилятора в F# для reference-типов?

MC>Т.е. встроить в язвк шорткат для fun s -> s.Method?


Это не "шорткат", а поддержка языком того, что заложено рантаймом (пусть только для reference-типов).
То есть можно было бы заметить вызов лямбды с вызовом метода внутри на прямой вызов нужного метода.
Почему нет? Далеко не факт что s.Method "в-инлайнится" в лямбду, зачем отказываться от бенефита?

Да и это бы делало F# более функциональным — методы экземпляра рассматривались бы как первоклассные функции.

MC>Это как раз работа для простого макроса. Как там в F# с метапрограммированием?


Плохо
Re[4]: [F#] Instance methods
От: Mr.Cat  
Дата: 12.09.09 14:33
Оценка:
Здравствуйте, Пельмешко, Вы писали:
П>Ага, только я ума не приложу почему должен за собой тащить FSharp.PowerPack.dll ради такой фундаментальной функции модуля String как length...
Microsoft, бессмысленный и беспощадный. Меня в реализации F# некоторые вещи просто-таки раздражают (от качества repl до таких вот извращений с паверпаками).
Re[5]: [F#] Instance methods
От: Mr.Cat  
Дата: 12.09.09 14:56
Оценка:
Здравствуйте, Пельмешко, Вы писали:
MC>>Т.е. встроить в язвк шорткат для fun s -> s.Method?
П>Это не "шорткат", а поддержка языком того, что заложено рантаймом (пусть только для reference-типов).
А твой пример точно работает? Под рукой дотнета нет, а msdn вот что говорит:

CreateDelegate(Type, MethodInfo) Creates a delegate of the specified type to represent the specified static method.


П>То есть можно было бы заметить вызов лямбды с вызовом метода внутри на прямой вызов нужного метода.

Ну по сути то, что ты привел, если оно работает, то это та же самая лямбда. И если оно работает быстрее — то оптимизированная лямбда — а оптимизация — работа для оптимизатора.

П>Далеко не факт что s.Method...

Не совсем понятно, что ты имел в виду
Re[6]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 15:15
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Здравствуйте, Пельмешко, Вы писали:

MC>>>Т.е. встроить в язвк шорткат для fun s -> s.Method?
П>>Это не "шорткат", а поддержка языком того, что заложено рантаймом (пусть только для reference-типов).
MC>А твой пример точно работает? Под рукой дотнета нет, а msdn вот что говорит:
MC>

MC>CreateDelegate(Type, MethodInfo) Creates a delegate of the specified type to represent the specified static method.

Ну я думаю, что не стал бы приводить примеры неработающего кода
Delegate.CreateDelegate() появился в .NET 2.0, с тех пор всё работает, да и Липперт подтвердит необычные свойства CreateDelegate() (там обратный пример — каррирование первого аргумента в target делегата).

П>>То есть можно было бы заметить вызов лямбды с вызовом метода внутри на прямой вызов нужного метода.

MC>Ну по сути то, что ты привел, если оно работает, то это та же самая лямбда. И если оно работает быстрее — то оптимизированная лямбда — а оптимизация — работа для оптимизатора.

Это не лямбда, а прямой указатель на instance-метод.

П>>Далеко не факт что s.Method...

MC>Не совсем понятно, что ты имел в виду

Сейчас приходится всё оборачивать в лямбды, как Вы и показали: fun s -> s.Method
Так вот и не факт что тело .Method() будет встроено (inline) в лямбду.
Если можно было бы сделать функциональный тип из .Method(), то никакого промежуточного вызова лямбды не было бы.

Вообще вопрос не в производительности, а концепции самой...
Мона было бы генерить те же лямбды за кулисами, почему нельзя ввести синтаксис какой-нибудь поудобнее чем лямбды каждый раз шлёпать...
Re[7]: [F#] Instance methods
От: Mr.Cat  
Дата: 12.09.09 15:44
Оценка:
Здравствуйте, Пельмешко, Вы писали:
П>Ну я думаю, что не стал бы приводить примеры неработающего кода
В моно тоже работает. Стало быть, баг в msdn? Или я что-то пропустил?

П>Это не лямбда, а прямой указатель на instance-метод.

В твоем примере там вообще делегат Func. И ты сам жаловался на то, что вызов делегата — тормозной. К тому же, этот Func потом надо будет обернуть в FastFunc. Не берусь судить, как та с производительностью, но есть подозрение, что в итоге выйдет в лучшем случае шило на мыло.

П>Вообще вопрос не в производительности, а концепции самой...

П>Мона было бы генерить те же лямбды за кулисами, почему нельзя ввести синтаксис какой-нибудь поудобнее чем лямбды каждый раз шлёпать...
Это, пожалуй, к разработчикам ocaml вопрос.
Re[8]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 12.09.09 16:03
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Здравствуйте, Пельмешко, Вы писали:

П>>Ну я думаю, что не стал бы приводить примеры неработающего кода
MC>В моно тоже работает. Стало быть, баг в msdn? Или я что-то пропустил?

Мэйнстрим не должен знать про такие хитрости, зачем документировать?

П>>Это не лямбда, а прямой указатель на instance-метод.

MC>В твоем примере там вообще делегат Func. И ты сам жаловался на то, что вызов делегата — тормозной. К тому же, этот Func потом надо будет обернуть в FastFunc. Не берусь судить, как та с производительностью, но есть подозрение, что в итоге выйдет в лучшем случае шило на мыло.

Я не предлагал подобным образом реализовывать FastFunc<>
В F# может потребоваться иметь дело и с делегатами, и с FastFunc, я лишь говорил, что в именно в случае делегатов фича заложена рантаймом.
Для FastFunc<> придётся, конечно же, как обычно генерить класс-наследник.

П>>Вообще вопрос не в производительности, а концепции самой...

П>>Мона было бы генерить те же лямбды за кулисами, почему нельзя ввести синтаксис какой-нибудь поудобнее чем лямбды каждый раз шлёпать...
MC>Это, пожалуй, к разработчикам ocaml вопрос.
Re[9]: [F#] Instance methods
От: nikov США http://www.linkedin.com/in/nikov
Дата: 12.09.09 16:16
Оценка: 10 (1)
Здравствуйте, Пельмешко, Вы писали:

MC>>В моно тоже работает. Стало быть, баг в msdn? Или я что-то пропустил?


П>Мэйнстрим не должен знать про такие хитрости, зачем документировать?


Всё документировано

The delegate type and the method must have compatible return types. That is, the return type of method must be assignable to the return type of type.

If firstArgument is supplied, it is passed to method every time the delegate is invoked; firstArgument is said to be bound to the delegate, and the delegate is said to be closed over its first argument. If method is static (Shared in Visual Basic), the argument list supplied when invoking the delegate includes all parameters except the first; if method is an instance method, then firstArgument is passed to the hidden instance parameter (represented by this in C#, or by Me in Visual Basic).

If firstArgument is supplied, the first parameter of method must be a reference type, and firstArgument must be compatible with that type.

Important Note:
If method is static (Shared in Visual Basic) and its first parameter is of type Object or ValueType, then firstArgument can be a value type. In this case firstArgument is automatically boxed. Automatic boxing does not occur for any other arguments, as it would in a C# or Visual Basic function call.
If firstArgument is a null reference and method is an instance method, the result depends on the signatures of the delegate type type and of method:

If the signature of type explicitly includes the hidden first parameter of method, the delegate is said to represent an open instance method. When the delegate is invoked, the first argument in the argument list is passed to the hidden instance parameter of method.

If the signatures of method and type match (that is, all parameter types are compatible), then the delegate is said to be closed over a null reference. Invoking the delegate is like calling an instance method on a null instance, which is not a particularly useful thing to do.

If firstArgument is a null reference and method is static, the result depends on the signatures of the delegate type type and of method:

If the signature of method and type match (that is, all parameter types are compatible), the delegate is said to represent an open static method. This is the most common case for static methods. In this case, you can get slightly better performance by using the CreateDelegate(Type, MethodInfo) method overload.

If the signature of type begins with the second parameter of method and the rest of the parameter types are compatible, then the delegate is said to be closed over a null reference. When the delegate is invoked, a null reference is passed to the first parameter of method.

Re[5]: [F#] Instance methods
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.09.09 05:54
Оценка:
Здравствуйте, Пельмешко, Вы писали:

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


G>>Здравствуйте, Пельмешко, Вы писали:


П>>>Не назвал бы его нормальным функциональным типом ввиду мультикастовости, к тому же в F# делегаты не используются, используется свой функциональны тип, FastFunc<T...>

G>>Он оборачивает обычный делегат.

П>Ничего подобного, откуда такое убеждение? Может стоит глянуть в реализацию перед тем как писать?

Неправльно выразился. Там делегатов нету.
Суть в том что подсовывать this в этот все равно код нету возможности.


G>>Через рефлексию вообще много чего сделать можно.

П>Рефлексия тут не при чём, я показал, что средствами CLR сценарий осуществим.
С помощью рефлексии. Без оной — не осуществим. Подсовывать this в рантайме честыми способами нельзя.

П>Компилятор C#, создавая делегат вызывает метод:

П>
П>  IL_000e:  newobj     instance void class [System.Core]System.Func`2<int32,int32>::.ctor(object, native int)
П>
Почему туда не передавать адрес instance-метода, не передавая object, раз рантайм поддерживает?

Что поддерживает? Ведь не получится подменить this у уже созданного Func.
А именно это и надо в конструкции Seq.map string.Length seq
Re[5]: [F#] Instance methods
От: Denom Украина  
Дата: 14.09.09 06:43
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

А что в repl'e не так?
... << RSDN@Home 1.2.0 alpha 4 rev. 1181>>
Re[6]: [F#] Instance methods
От: Пельмешко Россия blog
Дата: 14.09.09 08:17
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Пельмешко, Вы писали:


G>Неправльно выразился. Там делегатов нету.

G>Суть в том что подсовывать this в этот все равно код нету возможности.

В FastFunc<>? Там что угодно сгенерить можно, почему не так:
[Serializable]
internal class clo@9_1 : FastFunc<string, int>
{
    public override int Invoke(String s)
    {
        return s.Length;
    }
}
Вот Вам и функциональный тип из instance-метода, с подменой this, лямбда fun s -> s.Length то же самое сгенерит

G>>>Через рефлексию вообще много чего сделать можно.

П>>Рефлексия тут не при чём, я показал, что средствами CLR сценарий осуществим.
G>С помощью рефлексии. Без оной — не осуществим. Подсовывать this в рантайме честыми способами нельзя.

Наверное, мы понимаем под "подсовывать this" разное поведение.
Target уже созданного делегата честно изменить нельзя (сомневаюсь, что и рефлексией можно).
А вот сделать this явным первым параметром и передавать туда что угодно — можно.

Я могу привести пример создания такого делегата в msil через конструктор делегата типа System.Func`2<,>::.ctor(object, native int), как это делает компилятор C#, только не уверен, что Вы поверите, что это легально и без шайтан-рефлексии. Не понимаю, что такого невероятного в том, что instance-методы отличаются от статических только скрытым первым параметром и его можно вытащить напоказ?

П>>Компилятор C#, создавая делегат вызывает метод:

П>>
П>>  IL_000e:  newobj     instance void class [System.Core]System.Func`2<int32,int32>::.ctor(object, native int)
П>>
Почему туда не передавать адрес instance-метода, не передавая object, раз рантайм поддерживает?

G>Что поддерживает?

Рантайм поддерживает создание делегатов сигнатуры (A -> string -> int) из вот такого instance-метода:
class A {
    public int Foo(string x) { ... }
}
То есть A станет просто аргументом делегата, конкретный инстанс A надо будет передавать при вызове.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.