Здравствуйте, 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 карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Здравствуйте, 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(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.
Трурль wrote: > L>Поэтому, если в Немерле карринга нет, то там тоже неверно — нельзя сделать flip(f)(b,a) > Высказывание "в языке X карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Это понятно, семантической разницы нет, есть разница синтаксическая,
т.е. все сводится к удобству. Никто не запрещает написать Parsec или
применять монады направо и налево в, скажем, Эрланге, но никто же так не
делает. Или я ошибаюсь, и таки есть библиотеки комбинаторов для языков
без карринга?
Здравствуйте, Трурль, Вы писали:
Т>Высказывание "в языке X карринга нет" довольно таки бессмысленно.
Хорошо, нет автоматического карринга :-)
Хотя, если честно в Хаскеле его тоже нет. Там все функции уже каррированные (звучит как то опасненько).
Просто обычно (хотя и неверно) под каррингом понимается не собственно карринг, а поддержка каррированных функций в языке. В этом смысле это утверждение имеет смысл.
T> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Здравствуйте, 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(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.
lomeo, Вы писали:
T>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
L>Э-э-э... Тут не понял. Какая связь?
Здравствуйте, 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) с одним аргументом превращается в вызов с двумя, т.е.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
T>>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
L>>Э-э-э... Тут не понял. Какая связь?
LCR>Считается, что в Эрланге нет карринга:
поскипал
LCR>А оказывается есть: curry — это просто HOF.
Ух, как красиво. Всё больше убеждаюсь, что пора учить Эрланг. Очень уж он на лисп похож :)
LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.
Здравствуйте, 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)
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.
А "язык с каррингом" это язык, у которого + имеет тип int -> int -> int, а не int * int -> int.
Здравствуйте, 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)
Здравствуйте, 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, только мне они почему-то больше нравятся, несмотря на "кучу дополнительных значков":
Это все работает без каких-либо специальных библиотек, только набор простых правил для преобразования DCG в обычные предикаты(они обычно встроены в компилятор). Единственный минус — в отличие от Parsec, который специально создавался для построения парсеров, нет никаких дополнительных функций/предикат типа many1. Поэтому реализация предикаты num выглядит довольно сурово на первый взгляд:
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>Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.
Нам может и не нужно знать. А вот тому кто это будет потом читать? Да, даже если этим кем-то будешь ты сам, часто лучше оставить себе как можно больше "значков" о том как именно работает этот код.
Здравствуйте, 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)"?
Здравствуйте, 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 понимают, что идут рекурсивные вызовы.
Скорее всего, тут что-то другое. Но вот что?!
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
Здравствуйте, Трурль, Вы писали:
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
Т>
Ниче не понял, но очень интересно!
А почему так? Можешь в нескольких словах пояснить?