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)

Навеяно лисп и хаскель тредами.

holuiitipun
() автор топика

Никак, ФП не нужно )

xterro ★★★★★
()

А зачем это говно брать? В чистом js есть нативные map и filter, и прочие.

Вообще, тема бредовая даже для пятницы. Причем тут ФП?

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

В чистом js есть нативные map и filter, и прочие.

Они плохие!, не каррированые и методы, а не функции!

Причем тут ФП?

Функции высшего порядка, каррирование, вот это всё.

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

не каррированые и методы, а не функции!

Во первых, в JS методы не отличаются от функций.

Во вторых, как на этом твоем говне, строить нормальные пайпы?


print(
[1,2,3,4,5].map(function(x){return x*3})
.filter(function(x){return x%2===0})
.reduce(function(x, y){return x+y})
)
// 18

Функции высшего порядка, каррирование, вот это всё.

Сомневаюсь, что это имеет какое-то отношение к ФП, во всяком случае, в современном значении этого слова.

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

Во вторых, как на этом твоем говне, строить нормальные пайпы?

Добавил в пост про трансдьюсеры, как-то так:

transduce([(map (x) -> x * 2), (filter (x) -> x < 10), (map (x) -> x ** 2)]) [1..10]
holuiitipun
() автор топика

Нужно больше типов.

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

Го правую свёртку и левую через правую?

Готово:

reduce = (f) -> (xs) -> (m) -> if xs.length then reduce(f)(xs.slice 1)((f xs[0]) m) else m
coffee> reduce((x) -> (y) -> x + y)([1..10])(0)
55

_reverse = (m) -> (xs) -> if xs.length then _reverse([xs[0]].concat(m))(xs.slice 1) else m
reverse = _reverse []
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

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

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


def=function(op){return function(str){return this[op](Function("it", "return it"+str))}}

o=Array.prototype
o.map_=def("map")
o.filter_=def("filter")
o.reduce_=def("reduce")
delete o


print(
[1,2,3,4,5]
.map_("*3")
.filter_("%2===0")
)
// [ 6, 12 ]

Ненужно

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

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

Эм, я как бы про более высокие материи говорю, использовать тут js, coffee или питон - не принципиально.

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

Чтобы можно было как-то так:

twiceOdd = transduce [(filter (x) -> x % 2), (map (x) -> x * 2)]
coffee> twiceOdd [1..10]
[ 2,
  6,
  10,
  14,
  18 ]

reversedTwiceOdd = transduce [twiceOdd, reverse]
coffee> reversedTwiceOdd [1..10]
[ 18,
  14,
  10,
  6,
  2 ]

sumOfReversedTwiceOdd = transduce [reversedTwiceOdd, reducer(reduce)(0)((x) -> (m) -> x + m)]
coffee> sumOfReversedTwiceOdd [1..20]
200

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

Первая ссылка в гугле по запросу «трансдьюсеры»
http://habrahabr.ru/post/237613/

Ага, это оно, только в статье оно немного сложнее из-за обхода ограничения на размер стека и из-за js-way.

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

Примеры забавные, но причем тут «гуру» и «ФП»?

Все примеры с применением практик из ФП и написаны так, как пишут гуру ФП с лора.

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

Чтобы можно было как-то так:

Прежде чем я напрягусь, хотелось бы узнать, за каким хером это нужно? Есть подозрение, что ты решаешь через жопу то что можно сделать напрямую.


;[1,2,3,4,5,6,7,8,9,10]
.filter(function(x){return x%2})
.map(function(x){return x*2})
.reverse()

// [ 18, 14, 10, 6, 2 ]
и так далее.

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

Прежде чем я напрягусь, хотелось бы узнать, за каким хером это нужно? Есть подозрение, что ты решаешь через жопу то что можно сделать напрямую.

Профит в том, что можно разделять обработку на секции (каждая секция - трансдьюсер) и тестить их независимо друг от друга. И собирать из них разные вариации.

Ещё унификация работы с разными «источниками» данных (коллекции, каналы, очереди, пайпы и тд), но этого тут нет. Да и в js/coffee это как-то не сильно распространено, хотя в rx.js какие-то намёки есть.

holuiitipun
() автор топика

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

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

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

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

(каждая секция - трансдьюсер)

f: I => FI // трансдьсер, блять
g: FI => O // еще один, епт

g compose f // собираем из трансдьюсеров динамическую опердень

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

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

трансдьюсеры - это баззворд.

а, ччерт. манатки - тоже баззворд :(

anonymous
()

Без типов за ФП не считается.

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

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



aif=function(it, t, f){  if (eval(it)) return eval(t); eval(f)}

aif("1", "print(it+' is true')", "print(it+ ' is false')")
aif("0", "print(it+' is true')", "print(it+ ' is false')")

//  1 is true
//  0 is false

анафорический «макрос»

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

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

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


foo=1
side=function(){foo=2}
;(function(x){side(); x()})(function(){print(foo)})// сeмантически, это говно должно выдавать 1, потому что время вызова, до выполнения side
//  2

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

Обязательно. Только после того, как ты напишешь на хаскеле анафору.

А я-то думал, что мы о ленивости.

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

Анафоры это просто выразительное средство, что значит, зачем нужны? Чтобы код писать. Но если ты под «ленью» тут имеешь в виду лень такую как в хаскеле, т.е. нормальный порядок редукций, то там они, собственно невозможны. Это «фиктивная лень». Это, по сути, не лень вообще, с точки зрения юзера языка, так как с этой ленью он ничего не может сделать. Он знает о ней лишь потому, что вынужден плясать под ее дудку.

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

Хотелось бы пример конструкции, в котором они были бы полезны. Твой aif на том же Хаскеле выглядит намного проще:

aif p t f = if p then t else f

Ни строк, ни евала, красота.

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

Лень это просто выразительное средство, что значит, зачем нужна? Чтобы код писать. Но если ты под «анафорой» тут имеешь в виду анафору такую как в JS, то там она, собственно невозможна. Это «фиктивная анафора». Это, по сути, не анафора вообще, с точки зрения юзера языка, так как с этой анафорой он ничего не может сделать. Он знает о ней лишь потому, что вынужден плясать под ее дудку.

fixed

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

Так «it», благодаря ленивости, можно спокойно передать аргументом в t или f. Я тебя потому и спрашиваю, зачем в ленивом языке может пригодиться анафоричность.

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

Ты идиот или прикидываешься? Чтобы «передать» it тебе нужен нормальный язык, полноценный, который либо с макросами, либо с полноценным эвалом.

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

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

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