Re[6]: currying
От: Трурль  
Дата: 07.09.06 12:16
Оценка:
Здравствуйте, lomeo, Вы писали:

L> На самом деле (/2) о flip не знает

Hugs знает
>:t (/2)
flip (/) 2 :: Fractional a => a -> a
Re[7]: currying
От: Трурль  
Дата: 07.09.06 12:37
Оценка: :)
Здравствуйте, lomeo, Вы писали:

L>Надо как то так


L>
L>(define (flip f) (lambda (a b) (f b a)))
L>

Зачем же останавливаться на полпути?
(define (flip f) (lambda (a) (lambda ( b) (f b a))))


L>Поэтому, если в Немерле карринга нет, то там тоже неверно — нельзя сделать flip(f)(b,a)

Высказывание "в языке X карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Re[7]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 12:38
Оценка:
Здравствуйте, Трурль, Вы писали:

L>> На самом деле (/2) о flip не знает


Т>Hugs знает :)

Т>
>:t (/2)
Т>flip (/) 2 :: Fractional a => a -> a     
Т>


Прикольно! GHCi проверил — он не показывает такое.
Re[7]: currying
От: Vermicious Knid  
Дата: 07.09.06 12:46
Оценка: 34 (2)
Здравствуйте, Programmierer AG, Вы писали:

PA>Скорее def flip (f) = fun (x,y) { f(y, x) }

Нет, скорее def flip = f => (x,y) => f(y,x)

PA>Кстати, пример очень в тему, показывает, чем отличаются языки с

PA>каррингом от языков без .
Nemerle currying и не нужен, равно как и функция flip. Вместо (flip f 2), т.е. flip(f)(2, _) в нотации Nemerle, можно спокойно писать f(_,2).

Ну и скажем f(1,_,2) вместо чего-то вроде:
middle f a c = \b -> f a b c
middle f 1 2

А еще partial application в стиле Nemerle позволяет строить member-access выражения. Вроде _.SomeProperty или _.SomeFunc(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.
Re[8]: currying
От: Programmierer AG  
Дата: 07.09.06 12:48
Оценка:
Трурль wrote:
> L>Поэтому, если в Немерле карринга нет, то там тоже неверно — нельзя сделать flip(f)(b,a)
> Высказывание "в языке X карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Это понятно, семантической разницы нет, есть разница синтаксическая,
т.е. все сводится к удобству. Никто не запрещает написать Parsec или
применять монады направо и налево в, скажем, Эрланге, но никто же так не
делает. Или я ошибаюсь, и таки есть библиотеки комбинаторов для языков
без карринга?
Posted via RSDN NNTP Server 2.0
Re[8]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 12:50
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Высказывание "в языке X карринга нет" довольно таки бессмысленно.


Хорошо, нет автоматического карринга :-)
Хотя, если честно в Хаскеле его тоже нет. Там все функции уже каррированные (звучит как то опасненько).

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

T> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.


Э-э-э... Тут не понял. Какая связь?
Re[8]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 13:00
Оценка: 5 (1) +1 :)
Здравствуйте, Vermicious Knid, Вы писали:

VK>Nemerle currying и не нужен, равно как и функция flip. Вместо (flip f 2), т.е. flip(f)(2, _) в нотации Nemerle, можно спокойно писать f(_,2).


Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2)

VK>Ну и скажем f(1,_,2) вместо чего-то вроде:

VK>
VK>middle f a c = \b -> f a b c
VK>middle f 1 2
VK>


Ну какой нибудь комбинаторный злодей написал бы, что это B C или на Хаскель middle = (flip .)
Это так... Шутка.

Да, в Немерле это видно чётче. А что если нам надо функцию от двух аргументов?
f a b c = ...
zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b

Как такую функцию описать на Немерле?


VK>А еще partial application в стиле Nemerle позволяет строить member-access выражения. Вроде _.SomeProperty или _.SomeFunc(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.


Интересно, можно подробнее?
Re[9]: currying
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 07.09.06 13:24
Оценка: 13 (2) +2
lomeo, Вы писали:

T>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.


L>Э-э-э... Тут не понял. Какая связь?


Считается, что в Эрланге нет карринга:
curry(Fun, Arity) ->
    curry(Fun, Arity, []).

curry(Fun, 0, Args) ->
    apply(Fun, lists:reverse(Args));
curry(Fun, Arity, Args) ->
    fun (Arg) -> curry(Fun, Arity - 1, [Arg | Args]) end.

test() ->
    F0 = curry(fun (A, B, C, D) -> {A, B, C, D} end, 4),
    F1 = F0(1),
    F2 = F1(2),
    F3 = F2(3),
    {1, 2, 3, 4} = F3(4).

А оказывается есть: curry — это просто HOF.

Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[6]: currying
От: Mikl Kurkov Россия  
Дата: 07.09.06 13:42
Оценка: 4 (1) :)
Здравствуйте, ie, Вы писали:

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


VD>>Здравствуйте, Vermicious Knid, Вы писали:


А>>>>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того.

Т>>>>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10]
VK>>>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).

VD>>Вы бы пояснили смысл сказанного вами. А то боюсь, что многие не поняли что такое flip и зачем он нужен, но спросить стесняются.


ie>Ща попробую, как тут обычно делают


Дополню
J:

flip =: ~

Кстати вызов функции полученной применением наречия ~ (Reverse) с одним аргументом превращается в вызов с двумя, т.е.
x f~ y = y f x
f~ y   = y f y
Например:
  +~ 10
20

--
Mikl
Re[10]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 13:48
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

T>>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.


L>>Э-э-э... Тут не понял. Какая связь?


LCR>Считается, что в Эрланге нет карринга:

поскипал

LCR>А оказывается есть: curry — это просто HOF.

Ух, как красиво. Всё больше убеждаюсь, что пора учить Эрланг. Очень уж он на лисп похож :)

LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.


Ну да.
Re[9]: currying
От: Vermicious Knid  
Дата: 07.09.06 13:54
Оценка: 1 (1) +1
Здравствуйте, lomeo, Вы писали:

L>Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2)

Нет, не все равно. В Nemerle в отличие от Хаскель есть еще перегрузка по числу параметров и их типам, да еще и параметры по умолчанию. И карринг в духе Haskell или ML там совершенно не к месту. А решение, которое применяется в Nemerle и нагляднее(для всех, кроме комбинаторных злодеев), и не противоречит перегрузке.

L>zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b

L>Как такую функцию описать на Немерле?
zipWith(f(_,5,_), xs, ys)

Nemerle может и не очень краткий язык, но по крайней мере из этого кода ясно видно, что функция f принимает три параметра, и чтобы это понять не надо знать типы функций zipWith и f.

L>Интересно, можно подробнее?

А о чем тут собственно подробнее? _.SomeMethod(_) превращается в (x,y) => x.SomeMethod(y)
Re[10]: currying
От: Трурль  
Дата: 07.09.06 14:02
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.


А "язык с каррингом" это язык, у которого + имеет тип int -> int -> int, а не int * int -> int.
Re[10]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 14:13
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

L>>Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2)

VK>Нет, не все равно. В Nemerle в отличие от Хаскель есть еще перегрузка по числу параметров и их типам, да еще и параметры по умолчанию. И карринг в духе Haskell или ML там совершенно не к месту. А решение, которое применяется в Nemerle и нагляднее(для всех, кроме комбинаторных злодеев), и не противоречит перегрузке.

Пардон, я, конечно, говорил не о flip, а о карринге. Вы смотрели Parsec? Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?
На Хаскеле можно писать что то вроде

number = do
    s <- many1 digit
    return (read s)

expr = simpleExpr <|> addExpr

simpleExpr = number <|> paren

paren = do
    char '('
    x <- expr
    char ')'
    return x

и т.д.


Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.
Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.

L>>zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b

L>>Как такую функцию описать на Немерле?
VK>zipWith(f(_,5,_), xs, ys)

А если теперь так: \a b -> f b 5 a?

VK>Nemerle может и не очень краткий язык, но по крайней мере из этого кода ясно видно, что функция f принимает три параметра, и чтобы это понять не надо знать типы функций zipWith и f


Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.

L>>Интересно, можно подробнее?

VK>А о чем тут собственно подробнее? _.SomeMethod(_) превращается в (x,y) => x.SomeMethod(y)

А, понял. Спасибо.
Re[11]: currying
От: Vermicious Knid  
Дата: 07.09.06 16:05
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Пардон, я, конечно, говорил не о flip, а о карринге.

А я и о flip, и о карринге. В среде где нужно постоянно общаться с библиотеками, написанными на(и для) императивных языках с номинальной системой типов и при этом поддерживать вывод типов, системе типов Хиндли-Милнера не место.

L> Вы смотрели Parsec?

Да, смотрел я и Parsec, и про Packrat читал. И много еще чего, когда интересовался Хаскеллом. Пришел к выводу, что это все игрушки, созданные мечтателями с математическим складом ума. Парсить всего "тысячу строчек за секунду на современных машинах"(это почти цитата из документации по Parsec) это курам на смех, тут нечем гордиться, даже если это цитата десятилетней давности. К тому же я всегда считал, и буду считать дальше, что лучше писать парсеры вручную, используя традиционный алгоритм рекурсивного спуска + по необходимости backtracking.

L> Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?

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

L>На Хаскеле можно писать что то вроде

L>
L>number = do
L>    s <- many1 digit
L>    return (read s)

L>expr = simpleExpr <|> addExpr

L>simpleExpr = number <|> paren

L>paren = do
L>    char '('
L>    x <- expr
L>    char ')'
L>    return x
L>и т.д.
L>

Не очень понятно честно говоря. Этот пример еще ничего, в документации по Parsec есть еще и похуже. Концептуально мне это напомнило прологовские DCG, только мне они почему-то больше нравятся, несмотря на "кучу дополнительных значков":
% в прологе ; это |
expr(E) --> simpleExpr(E); addExpr(E).
simpleExpr(E) --> num(E); "(", expr(E), ")".
addExpr(X + Y) --> simpleExpr(X), "+", expr(Y).

Это все работает без каких-либо специальных библиотек, только набор простых правил для преобразования DCG в обычные предикаты(они обычно встроены в компилятор). Единственный минус — в отличие от Parsec, который специально создавался для построения парсеров, нет никаких дополнительных функций/предикат типа many1. Поэтому реализация предикаты num выглядит довольно сурово на первый взгляд:
num(N) --> digit(D0), digits(D), { number_chars(N, [D0|D]) }.
digits([D|T]) --> digit(D), !, digits(T).
digits([]) --> [].
digit(D) --> [D], { code_type(D, digit) }.

А с предопределенными many1 и digit все так же просто, как и на Haskell:
num(N) --> many1(digit,Ds), { number_chars(N, Ds) }


L>Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.

Ну и. А в Прологе функций вообще нет, не то что каррированных. При этом выглядит все весьма понятно. На Nemerle можно написать макрос, который будет строить парсер вообще из BNF или EBNF(кое-что подобное, но не очень удобное уже есть). А можно подумать и сделать что-то отдаленно напоминающее Parsec и без макросов, но не использующее карринг ни в каком виде(все таки в Nemerle есть объекты и определяемые операторы).

L>Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.

Может быть и выгодно, но выгода это не настолько велика. Вот на OCaml пишется крайне много парсеров, при этом карринг там есть, но в этих парсерах я его осознанного и целенаправленного применения(в смысле всяких комбинаторов итп) не видел.

L>А если теперь так: \a b -> f b 5 a?

Это вроде нельзя(без flip), а смысл? Я вообще-то и partial application, если честно, практически никогда на Nemerle не применял. И в этом случае мне проще написать (a,b) => f(b,5,a), я символы за счет потери читабельности не люблю экономить.

L>Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.

Нам может и не нужно знать. А вот тому кто это будет потом читать? Да, даже если этим кем-то будешь ты сам, часто лучше оставить себе как можно больше "значков" о том как именно работает этот код.
Re[11]: currying
От: Трурль  
Дата: 07.09.06 18:41
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Здесь нет ни одной не каррированной функции


А по коду этого сразу и не скажешь.
Re[12]: currying
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 07.09.06 20:06
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

L>>Пардон, я, конечно, говорил не о flip, а о карринге.


VK>А я и о flip, и о карринге. В среде где нужно постоянно общаться с библиотеками, написанными на(и для) императивных языках с номинальной системой типов и при этом поддерживать вывод типов, системе типов Хиндли-Милнера не место.


Не понял при чём здесь это.

L>> Вы смотрели Parsec?


VK>Да, смотрел я и Parsec, и про Packrat читал. И много еще чего, когда интересовался Хаскеллом. Пришел к выводу, что это все игрушки, созданные мечтателями с математическим складом ума. Парсить всего "тысячу строчек за секунду на современных машинах"(это почти цитата из документации по Parsec) это курам на смех, тут нечем гордиться, даже если это цитата десятилетней давности. К тому же я всегда считал, и буду считать дальше, что лучше писать парсеры вручную, используя традиционный алгоритм рекурсивного спуска + по необходимости backtracking.


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

L>> Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?


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


Ну вот пожалуйста — одна и та же задача решается каррингом и макросами. Что предпочесть?

VK>Не очень понятно честно говоря. Этот пример еще ничего, в документации по Parsec есть еще и похуже. Концептуально мне это напомнило прологовские DCG, только мне они почему-то больше нравятся, несмотря на "кучу дополнительных значков":


Почему? Если это одно и то же, но в прологовском коде еще и дополнительные параметры у функций?

L>>Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.


VK>Ну и. А в Прологе функций вообще нет, не то что каррированных. При этом выглядит все весьма понятно. На Nemerle можно написать макрос, который будет строить парсер вообще из BNF или EBNF(кое-что подобное, но не очень удобное уже есть). А можно подумать и сделать что-то отдаленно напоминающее Parsec и без макросов, но не использующее карринг ни в каком виде(все таки в Nemerle есть объекты и определяемые операторы).


Ну хорошо. Опять — одна и та же задача решается каррингом и объектами+операторами. Что проще?

L>>Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.


VK>Может быть и выгодно, но выгода это не настолько велика. Вот на OCaml пишется крайне много парсеров, при этом карринг там есть, но в этих парсерах я его осознанного и целенаправленного применения(в смысле всяких комбинаторов итп) не видел.


Разные есть библиотеки, но я понял.

L>>А если теперь так: \a b -> f b 5 a?

VK>Это вроде нельзя(без flip), а смысл? Я вообще-то и partial application, если честно, практически никогда на Nemerle не применял. И в этом случае мне проще написать (a,b) => f(b,5,a), я символы за счет потери читабельности не люблю экономить.

ОК.

L>>Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.

VK>Нам может и не нужно знать. А вот тому кто это будет потом читать? Да, даже если этим кем-то будешь ты сам, часто лучше оставить себе как можно больше "значков" о том как именно работает этот код.

Т.е. "negates xs = filter (\x -> x < 0) xs" лучше, чем "negates = filter (<0)"?
Re[13]: currying
От: VladD2 Российская Империя www.nemerle.org
Дата: 08.09.06 00:11
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Т.е. "negates xs = filter (\x -> x < 0) xs" лучше, чем "negates = filter (<0)"?


Чтобы не трепаться в пустую... Вот кусок кода который я написал вчера для проекта интеграции:
foreach ((name, lst) when lst.Exists(_ is MethodBuilder) in _extension_methods.KeyValuePairs)
    ...


"_ is MethodBuilder" превращается в нужную фукнцию. Точно так же вместо твоего negates можно просто написать "filter(_ < 0)".
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: currying
От: ie Россия http://ziez.blogspot.com/
Дата: 08.09.06 05:41
Оценка:
Здравствуйте, Programmierer AG, Вы писали:

PA>ie wrote:

>>
>> let rec flip f b a = f a b
PA>rec лишний
+1

Хорошо, что напомнили По поводу rec давно хотел просвятиться. Итак, что пишут в мануале:

Function definitions may be recursive, provided this is requested explicitly, using the keyword rec:
let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;


А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен? (небыло бы я бы и не написал лишний rec )
Хинт компилятору о том что будут рекурсивные вызовы? Да нет, конечно, типы выводить умеем, а распознать рекурсию не сможем --- смешно.
Декларация разработчика о намерениях? Тоже, ИМХО, бред. На всех языках пишут рекурсии и как-то без rec понимают, что идут рекурсивные вызовы.
Скорее всего, тут что-то другое. Но вот что?!
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Re[8]: currying
От: Трурль  
Дата: 08.09.06 06:36
Оценка:
Здравствуйте, ie, Вы писали:


ie>А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен?

# let fib n = 1;;
val fib : 'a -> int = <fun>
# fib 8;;
- : int = 1
# let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
val fib : 'a -> int = <fun>
# fib 8;;
- : int = 2
# let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
val fib : int -> int = <fun>
# fib 8;;
- : int = 34
Re[9]: currying
От: ie Россия http://ziez.blogspot.com/
Дата: 08.09.06 06:57
Оценка:
Здравствуйте, Трурль, Вы писали:

ie>>А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен?

Т>
Т># let fib n = 1;;
Т>val fib : 'a -> int = <fun>
Т># fib 8;;
Т>- : int = 1
Т># let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>val fib : 'a -> int = <fun>
Т># fib 8;;
Т>- : int = 2
Т># let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>val fib : int -> int = <fun>
Т># fib 8;;
Т>- : int = 34
Т>

Ниче не понял, но очень интересно!
А почему так? Можешь в нескольких словах пояснить?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.