Pull to refresh

Comments 39

Буквально, свежак:
«Итак, вы можете писать что угодно на чём угодно, в смысле программирования, и монады у вас там будут независимо от того, в курсе вы или нет. „
http://ivan-gandhi.livejournal.com/1884945.html
Хорошая статья. Только в данном случае целью была явная реализация в целях демонстрации.
Да у меня ж только к заголовку. И не то, чтобы придирка. А статье только плюс.
Даешь монады на брейнфаке!
Каррирование != частичное применение
Тут вы правы. Скорректировал. В данном случае именно частичное применение, с замыканием части параметров и возвратом функции, принимающей оставшиеся. Каррирование (нормальное) в Python страшновато выглядит.
Если не сложно, объясните в чем разница.
Думаю в том, что каррирование можно применять много раз подряд пока не наберется требуемое кол-во аргументов. Частичное применение работает до первого раза.
В питоне без хаков нельзя узнать сколько аргументов требуется функции.
можно: f.func_code.co_argcount
А вот за это спасибо большое! Единственно, неудобно узнавать кол-во параметров у метода — self тоже считается.
unbound-методы действительно принимают на один аргумент больше (+self), чем соответствующие им bound-методы:

class A(object):
  def m(self, a, b, c):
    pass


A.m — unbound-метод, принимающий 4 аргумента (self, a, b, c)

Для а = A(), A().m — это bound-метод, принимающий 3 аргумента (a, b, c), по-сути тоже самое что partial(A.m, a)

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

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

или каррирование не до конца — несчитово?
Каррированная функция, это всегда функция одного аргумента, причем всегда первого. Тут ноги растут из лямбда-исчисления, может я и не смогу правильно объяснить — моё понимание, скорее, интуитивно.
Частичное применение возможно везде, где есть замыкание и функции, как объекты первого порядка.
При частичном применении мы замыкаем часть параметров и возвращаем обычную функцию, просто с меньшим кол-вом параметров.

Каррированная функция каррирована насквозь:
f = lambda x: lambda y: lambda z: (x+y)*z
Такую функцию можно вызывать(для получения конечного результата) только так:
f(1)(2)(3)
На Python это некрасиво выглядит, а вот в Haskell всё отлично — там все функции каррированы всегда, и вызов выглядит проще:
f 1 2 3 == (((f 1) 2) 3)
Книга действительно отличная. Впечатляет, что написал ее 23-летний словенец (или как называется житель Словении).
Особенно, примеры и последовательность подачи материала удачны.
Последовательность: Maybe, IO (как magic-штуки в конкретных задачах) -> функторы -> аппликативные функторы -> монады. В процессе подачи материала та же Maybe сама вырисовывается и пишется с нуля: вот тут то и приходит понимание
Автор сразу начал реализовывать, но так и не написал, что же представляют из себя эти загадочные монады :)
Интересно? Могу написать последовательно, начиная с функторов, с примерами. Есть желание адаптировать «Learn you a Haskell for great Good», уже в Python-ключе и для императивщиков
Да, было бы замечательно.
Отлично, скоро будет статья.
Про то, что же они из себя представляют понаписано уже море статей. В том числе на хабре.
Статьи танцуют и от теории категорий, и от монадических значений, и от пирмеров монад «в реальной жизни» — яснее от этого не становится нифигашеньки.

Автор пошёл альтернативным путём — и это хорошо!

А я как-то делал композицию функций на Питоне. Чтобы можно было соединять функции оператором | и писать что-то вроде:
','.join(map(str | int, [4.5, 6.7, 8.02]))

Получается прикольно, но использовать такое, конечно, не особенно станешь.
Тут сказывается замороженный синтаксис Python — ничего нового не добавишь, только перегрузка и остается ))
А в вашем примере можно и не перегружать пайп, даже читабельнее будет:
def push_through(*funcs):
    def inner(value):
        return reduce(lambda x, f: f(x), funcs, value)
    return inner

print ','.join(map(push_through(int, str), [4.5, 6.7, 8.02]))

Только тут функции идут в порядке их применения, а не в обратном порядке, как в композиции. Здесь происходит «проталкивание» данных через последовательность обработчиков. Порядок применения функций, само собой, можно и развернуть.
Методом обёртывания функций?
Это же не обёртывание:
sequence = lambda *funcs: lambda x: reduce(lambda xx, f: f(xx), funcs, x)
map(sequence(int, str), [1.0, 2.5, 3.7])

Тут вместо композиции свертка с применением списка функций к значению. Результат тот же будет
В этом случае нужно. Причем, случай крайний — для встроенных функций просто так оператор не перегрузишь, и декоратор не прикрутишь. Тут только так:
Combinable(int) | str
или
str | Combinable(int)
или хотя бы
str | int | Combinable()
Ну или так:
def wrap(module):
    for i in dir(module):
        obj = getattr(module, i)
        if callable(obj):
            setattr(module, i, Composable(obj))
wrap(__builtins__)

:-)
Ну это уже совсем неявно, а «явное лучше неявного» ))
«result must be MayBe» — это пять! это по-хаскелевски! :)
Результат должен или правильно быть или правильно не быть ))
По-моему на Хабре нехватает хорошего туториала, «на пальцах» объясняющего, что такое монады, моноиды, функторы, аппликативные функторы и остальные понятия и принципы из этой области. Есть, конечно, Википедия, но по-моему там не достаточно понятно. Вот, например, что такое MayBe я прекрасно понимаю и активно использую (точнее Option в Scala), а что такое монада в принципе и всё остальное — нет.
В замечательной книге «Learn You a Haskell for Great Good!» отлично всё написано про монады, моноиды, функторы. Причем описание идёт на практических примерах — без непосредственного введения термина, скажем «монада», но с постепенным «выкристализовыванием» чего-то простого, понятного, а главное — полезного. А потом это «что-то» и оказывается монадой ))

Я думаю, не стоит цитировать книгу на Хабре — она есть в свободном доступе (по крайней мере английская версия). Своими словами можно объяснить, но лучше чем в книге у меня лично вряд ли получится…

P.S. Option в Scala, это, всё таки, просто АТД, в который удобно завернуть результат вычисления, способного неудасться. И всё. Синтаксической поддержки нет (удобной, хотя бы как в Haskell), вычисления в контексте монадном почти никто не делает…
Если уж монады в Scala искать, так лучше обратить внимание на for/yield — вполне себе, монада списка сдобренная нужным кол-вом синтаксического сахара.

P.P.S. Объяснение «на пальцах» выше указанным терминам можно дать в практическом ключе, а фундамент находится в теории категорий, которую сходу на пальцах не объяснишь…
а фундамент находится в теории категорий, которую сходу на пальцах не объяснишь

Опыт многократно показал, что почти любую академическую заумь можно объяснить так, чтобы было понятно даже детям. Было бы желание и достаточное понимание у самого объясняющего. Ждём «теорию категорий для чайников» на Хабре… :-)

Если уж монады в Scala искать, так лучше обратить внимание на for/yield — вполне себе, монада списка сдобренная нужным кол-вом синтаксического сахара.

Так и не понял зачем нужен for/yield когда есть map. Кажется только один раз мне пришлось использовать for/yield и то я уже не помню почему.
В себе пока не чувствую сил теоркат объяснять, увы…

А for/yield это не просто map, это скорее filter/map/concat в одном флаконе:
class Book(title: String, authors: List[String])

// наименования книг, написанных Кнутом
for (b <- books; a <- b.authors if a startsWith "Knuth,") yield b.title

// авторы, написавшие более чем одну книгу
{ for {
    b1 <- books
    b2 <- books
    if b1 != b2
    a1 <- b1.authors
    a2 <- b2.authors
    if a1 == a2
  } yield a1
}.distinct
Sign up to leave a comment.

Articles