LINUX.ORG.RU

Как стать гуру ФП

 , ,


0

1

Берёшь модно выглядящий ЯП, например coffeescript, пишешь:

size = (xs) -> xs.length
concat = (xs) -> (ys) -> xs.concat(ys)

map = (f) -> (xs) ->
    [y, ys...] = xs
    if (size xs)
        (concat [f y]) ((map f) ys)
    else
        []

Пробуешь:

coffee> (map (x) -> x * 2) [1..5]
[ 2, 4, 6, 8, 10 ]

Потом пишешь filter:

filter = (f) -> (xs) ->
    [y, ys...] = xs
    if not (size xs)
        []
    else if (f y)
        (concat [y]) ((filter f) ys)
    else
        (filter f) ys

Пробуешь:

coffee> filter((x) -> x > 5) [1..10]
[ 6, 7, 8, 9, 10 ]

Теперь ты знаешь ФП лучше среднего хаскелиста и лиспера лора!

А ещё благодаря такому подходу легко можно реализовать модные сейчас трансдьюсеры:

transduce = (fns) -> (xs) ->
    [f, fs...] = fns
    if (size fns) 
        (transduce fs)(f xs)
    else
        xs

Использование:

coffee> transduce([(map (x) -> x * 2), (filter (x) -> x < 10), (map (x) -> x ** 2)]) [1..10]
[ 4, 16, 36, 64 ]

Свёртки:

reduce = (f) -> (xs) -> (m) ->
    [y, ys...] = xs
    if (size xs)
        ((reduce f) ys)((f y) m)
    else
        m

coffee> reduce((x) -> (y) -> x + y)([1..10])(0)
55

reverse = (xs) ->
    aux = (m) -> (ys) ->
        [z, zs...] = ys    
        if (size ys)
            (aux (concat([z]) m)) zs
        else
            m

    (aux []) xs

coffee> reverse [1..5]
[ 5, 4, 3, 2, 1 ]

reducer = (r) -> (m) -> ((f) -> (xs) -> r(f)(xs)(m))
reduceLeft = (f) -> (xs) -> (m) -> transduce([reverse, reducer(reduce)(m)(f)])(xs)
coffee> reduceLeft((x) -> (m) -> x + m)([1..10])(0)
55

reducer нужен для того чтобы reduce(Left) имел интерфейс трансдьюсера (как у map и filter вызванные с функцией, и собственно transduce).

update: Самое главное не забывать называть переменные f, xs, x и тд.

update 2: И да, легко получить RangeError: Maximum call stack size exceededю

update 3: Превратил однострочники в многострочникию



Последнее исправление: holuiitipun (всего исправлений: 8)
Ответ на: комментарий от vimquest

Давай я тебе покажу. Только настоящую лень, а не быдляцкую, как в хаскеле

Это не лень, а pass-by-name для любителей eval.

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

иди читай что такое анафора. Пример Анафоры: «Маша устала, она хочет спать». Пока не напишешь на своем говне это, не возвращайся сюда, ты достал уже своей бестолковщиной.

Ты напиши сперва пример, где это существенно.

Waterlaz
()
Ответ на: комментарий от vimquest

Погоди, но ведь твой предыдущий пример - это тривиальный однострочник на Хаскеле. Без строк, евала и макросов.

Я всего лишь попросил показать что-то более полноценное и выразительное.

anonymous
()
Ответ на: комментарий от anonymous

на Хаскеле

Как ты заебла уже тупая овца. Блять, трудно поверить, что такие дауны вообще существуют. Такие наверное только среди хаскелистов встречаются.

anonymous
()
Ответ на: комментарий от vimquest

Давай я тебе покажу. Только настоящую лень, а не быдляцкую, как в хаскеле

VM1772:1 Uncaught TypeError: Illegal invocationmessage: "Illegal invocation"stack: (...)get stack: function () { [native code] }set stack: function () { [native code] }__proto__: Error
holuiitipun
() автор топика
Ответ на: комментарий от vimquest

если бы ты писал читабельный код, я бы ещё попытался его осилить, а так это какое-то ехал function через function

anonymous
()
Ответ на: комментарий от holuiitipun

хз, что там у тебя. Ты print хоть догадался определить? ф-ции print как бы, нет в стандарте. Пиши print=console.log.bind(console), может в этом дело. Ты где проверяешь?

vimquest
()

Coffeescript - ваще рульный язык, на самом деле :-) Его бы сделать компилируемым...хотя бы в jvm bytecode.

menangen
()
Ответ на: комментарий от holuiitipun

Знаю, но это не jvm byte code :-) Хотелось бы такой же coffescript со статической типизацией по типу Scala, но не такой вычурный.

menangen
()
Ответ на: комментарий от Reset

Приведи пример «гибкости», что ты под этим имешь в виду?

пайп |> (в F# и ocaml) и , вроде, $ в haskell.

В слабых языках есть гибкость, а в мощном нет??? LOL

vimquest
()
Ответ на: комментарий от vimquest

В моем случае все аргументы равноправны. В haskell можно делать каррирование по первому аргументу, а у тебя в качестве первого аргумента (до точки) всегда должен стоять объект.

Reset 🤡🤡
()
Ответ на: комментарий от Reset

В haskell можно делать каррирование по первому аргументу

Оно не нужно внормальном ЯП. И пишется на коленке за секунду.

в качестве первого аргумента (до точки) всегда должен стоять объект.

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

vimquest
()
Ответ на: комментарий от vimquest

В слабых языках есть гибкость, а в мощном нет??? LOL

Это я не распарсил. Пайп в ml-based языках не является языковой конструкцией, а является обычной функцией, которая определяется примерно так

fun |> x f = f x
Двуместные функции можно вызывать в инфиксной форме, это уже свойство языка, поэтому можно сделать такой вызов.
x |> f

Отличие от твоей точки только в гибкости.

Reset 🤡🤡
()
Ответ на: комментарий от vimquest

Оно не нужно внормальном ЯП. И пишется на коленке за секунду.

Напиши.

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

У которого должен быть метод. Выглядит очень криво и несимметрично.

Reset 🤡🤡
()
Ответ на: комментарий от Reset

Оппа, вот мы и раскрыли вопрос сисек гибкости. То о чем ты говоришь, это вообще не пайп, а жалкая пародия. Ты пржеде чем применить его, должен сначала определить его. Это ПАЙП??? Данунах. Вот бы тебе в шелле такой пайп, яб поржал. Хотя я и так ржунимогу.

vimquest
()
Ответ на: комментарий от Reset

должен быть метод

Мы о массивах говорим. У всех массивов есть по дефолту все методы работы с массивами. Ты не гони туфту, это бредятина какая то. А то ведь, получается, чтобы вызвать ф-цию с аргументом, надо вначале, подать этот аргумент туда, вот облом то.

vimquest
()
Ответ на: комментарий от vimquest

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

С чего бы это? Это как раз самый настоящий пайп.

Ты пржеде чем применить его, должен сначала определить его.

В большинстве реализаций он определен в стандартной библиотеке. Даже если его надо определить, то это делается один раз и я не понимаю почему это недостаток.

Reset 🤡🤡
()
Ответ на: комментарий от vimquest

Мы о массивах говорим.

А я говорю о произвольных объектах и функциях. У меня в цепочке массив может превратиться во что угодно. И где в итоге гибче? Очевидно, что не у тебя.

Reset 🤡🤡
()
Ответ на: комментарий от Reset

А я говорю о произвольных объектах и функциях

нет там никаких проблем с этим


Object.prototype.callme=function(f){return f.call(this.valueOf())}

console.log(
"foo".callme(function(){return this+this})
)

//  foofoo

можно строить произвольные цепочки произвольной длины с произвольными типами. Короче, гибкость 100%, как и положено прототипному яп. Чего не скажешь о том говне которое ты приводишь, где без явной передачи аргументов не обойдешься. Статика и слабость.

vimquest
()
Ответ на: комментарий от vimquest

нет там никаких проблем с этим

То есть прежде чем писать то что ты называешь «пайпом» надо написать кучу таких портянок? Дооо, это «гибкость» ....

Чего не скажешь о том говне которое ты приводишь, где без явной передачи аргументов не обойдешься.

ЩИТО?

Статика и слабость.

Где слабость?

Reset 🤡🤡
()
Ответ на: комментарий от Reset

Это говно, а не пайп В нормальных языках есть пайп |> (в F# и ocaml) и , вроде, $ в haskell.

Это круче пайпа, оно реюзабельно и монадка. Ну и оно немного для другого =)

holuiitipun
() автор топика
Ответ на: комментарий от Reset

Пайп в ml-based языках не является языковой конструкцией, а является обычной функцией, которая определяется примерно так

Надо будет как-нибудь поделать что-нибудь на ocaml.

holuiitipun
() автор топика
Ответ на: комментарий от Reset

надо написать кучу таких портянок

Какую кучу? Проснись, эта строка делает все, больше ничего не надо писать.


Object.prototype.callme=function(f){return f.call(this.valueOf())}
Object.prototype.show=function(){var o=this.valueOf(); console.log(o); return o}

"foo"
.callme(function(){return this+this})
.show()
.replace(/foo/, "bar")
.show()
.split("")
.show()
.concat([1,2,3])
.show()
.slice(-3)
.show()
.reduce(function(x, y){return x+y})
.show()

// ::: foofoo
// ::: barfoo
// ::: [ 'b', 'a', 'r', 'f', 'o', 'o' ]
// ::: [ 'b', 'a', 'r', 'f', 'o', 'o', 1, 2, 3 ]
// ::: [ 1, 2, 3 ]
// ::: 6

Где слабость?

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

vimquest
()
Ответ на: комментарий от nikolnik

Зачем извращаться и заново изобретать Ruby?

В каком месте здесь ruby?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

Какую кучу? Проснись, эта строка делает все, больше ничего не надо писать.

А если тебе в одном модуле нужен show с одним поведением, а в другом - с другим?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

Я тебе уже написал что является более гибким аналогом твоей точки. Соответственно замени свою точку на $ или |> и будет аналог.

Reset 🤡🤡
()
Ответ на: комментарий от Reset

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

vimquest
()
Ответ на: комментарий от Reset

уже написал что является более гибким аналогом твоей точки

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

Я комбинирую любые объекты в произвольном порядке, без каких=либо костылей.

vimquest
()
Ответ на: комментарий от vimquest

Тебе следует подучить матчасть прежде чем так громко пердеть в лужу.

Reset 🤡🤡
()
Ответ на: комментарий от vimquest

Без проблем. Каждый модуль унаследует свою версию.

Попробую объяснить понятней.

Есть библиотека zzlib, в которой определено:

Object.prototype.show = function(){
    var self = this.valueOf();
    document.write(self.text);
    return self;
}

И твой код с:

Object.prototype.show = function(){
    var self = this.valueOf();
    console.log(self);
    return self;
}

И всё, ты сломал библиотеку.

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

что это якобы гибче, однако, в чем заключается гибкость, я так и не увидел.

В том, что все аргументы равноправны и я могу их связывать со значениями как хочу.

То говно, которое надо предварительно декларировать — это не пайп, а просто говно.

Это не аргумент, а кукареканье. Еще раз для слабоумных повторю, что он определен в стандартной библиотеке и от типа не зависит.

Я комбинирую любые объекты в произвольном порядке, без каких=либо костылей.

Выше ты пел о другом, а как только слился на массивах так начал выкручиваться.

Reset 🤡🤡
()
Последнее исправление: Reset (всего исправлений: 1)
Ответ на: комментарий от holuiitipun

Ты вообще не понимаешь, как работает Ъ OOP. У тебя каждый модуль — это объект, который наследует свои методы. Имена не пересекаются. Я могу например, в верхнем объекте определить метод call, но он не затрет метод call функции, все будет работать нормально.


Object.prototype.call=function(f){return f.call(this.valueOf())}
Object.prototype.show=function(){var o=this.valueOf(); console.log(o); return o}


"foo".call(function(){return this.split("")}).show()

;(function(x){return x + x}).call(null, 1).show()
//  [ 'f', 'o', 'o' ]
//  2
Все объекты обзавелись новым методом call, но метод call функций никуда не делся.

На уровне модулей, однако, расширять главный прототип не стоит.

vimquest
()
Ответ на: комментарий от Reset

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

vimquest
()
Ответ на: комментарий от vimquest

Все объекты обзавелись новым методом call, но метод call функций никуда не делся.

Ну да, а если ты в обоих модулях вешаешь метод на Object.prototype?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

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

Я хоть ml не знаю, но могу показать на clojure:

(->> (string/split " " "word by word")
     (map #(str % "!"))
     (filter (compliment "by!"))
     (string/join "/"))

>> "word!/word!"

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

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

Но ты именно так и предлагаешь делать. А с пайпами такой проблемы нет.

Ну и всё таки стоит понимать, что pipe и method chaining(fluent interface) это немного разное.

holuiitipun
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.