Функциональное программирование 2 - о синтаксисе

Прежде чем переходить к возможностям функциональных объектов, хотелось бы рассмотреть варианты синтаксиса для делегатов. Кроме рассмотренного уже синтаксиса указателей на функции, в разных языках программирования существуют разные по степени удобства варианты синтаксиса для делегатоподобных объектов.

Рассмотрим передачу делегатов в функции.

В C#, прежде чем использовать делегат как аргумент функции, нужно сначала объявить тип делегата. Например так:

delegate int MyDelegate(string s);

Здесь MyDelegate - тип, значениями которого могут быть любые методы, принимающие string и возвращающие int. Использовать его можно например так:

public void Process( MyDelegate  func)
{
    foreach (string s in someList)
    {
       func(b);
    }
}

В D тип делегата можно не объявлять, а объявлять прямо по месту использования.
int foo(int delegate(int x) dg)
{
    return dg(10) + 1;
}

В ObjC 2.0 существует аналогичное понятие "блоки". Подобно C# (и Си) можно определить тип блока (очень похоже на указатель на функцию):

typedef void (^MyBlock)(int);

Хотя можно определять блок и без типа. Например, функция  logBlock в качестве аргумента принимает другую функцию, принимающую int и возвращаюшую NSString*

void logBlock( NSString * ( ^theBlock )( int ) )
{
    NSLog( @"Block returned: %@", theBlock() );
}

В скриптовых языках все несколько проще. Объявлять типы не надо (так как это языки как правило с динамической типизацией).

function foo( callback )
{
    alert( callback() );
}
function bar()
{
    var str = 'hello, world';
    foo ( function() { return str; } );
}
bar();

В этом примере мы видим еще одну возможность функционального программирования - лямбда-функции, т.е. функции, определенные прямо в месте их использования (передачи в качестве аргумента). Такая возможность есть во многих языках с элементами ФП, но ее мы рассмотрим далее.

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

public static void Sort[T](ary : array[T], comparison : T * T -> int);

или Scala:

def sum(f: Int => Int, a: Int, b: Int): Int

В обоих примерах тип делегата задавался максимально просто и естестенно: "список входных параметров", "стрелочка", "список возвращаемых параметров". Ничего лишнего, все максимально наглядно и прозрачно. Именно такой вариант синтаксиса и взят за основу в Neo.



No comments:

Post a Comment