Борьба с использованием чисто функционального программирования для решения повседневных задач - PullRequest
26 голосов
/ 31 мая 2011

Я видел это сообщение в хакерских новостях сегодня. Я борюсь с теми же проблемами понимания того, как чисто функциональное программирование поможет мне абстрагироваться от реальной проблемы. Я сделал переход от обязательного к программированию ОО 7 лет назад. Я чувствую, что справился с этим, и он хорошо послужил мне. За последние пару лет я изучил некоторые приемы и концепции в функциональном программировании, такие как отображение и уменьшение, и мне они тоже нравятся. Я использовал их в своем OO-коде и был доволен этим, но, абстрагируя набор инструкций, я могу думать только об OO-абстракциях, чтобы сделать код красивее.

Недавно я работал над проблемой в python и пытался избежать использования ОО для ее решения. По большей части мое решение выглядит крайне необходимым, и я знаю, что смог бы сделать его красивым и чистым, если бы использовал OO. Я думал, что опубликую проблему, и, возможно, функциональные эксперты могут предложить решение, красивое и функциональное. Я могу опубликовать свой уродливый код, если я должен, но предпочел бы нет. :) Вот в чем проблема:

Пользователь может запросить изображение или эскиз изображения. Если пользователь запрашивает эскиз изображения, а его еще нет, создайте его с помощью модуля PIL в Python. Также создайте символическую ссылку на оригинал или миниатюру с удобочитаемым путем, поскольку имя исходного изображения является хеш-кодом, а не описывает его содержимое. Наконец, перенаправьте на символическую ссылку этого изображения.

В OO я, вероятно, создал бы базовый класс SymlinkImage, подкласс ThumbnailSymlinkImage и подкласс OriginalSymlinkImage. Общие данные (в классе SymlinkImage) будут такими, как путь к оригиналу. Общее поведение будет создавать символическую ссылку. Подклассы будут реализовывать метод, называемый чем-то вроде «generate», который будет отвечать за создание миниатюры, если это применимо, и за вызов их суперкласса для создания новой символической ссылки.

Ответы [ 3 ]

20 голосов
/ 31 мая 2011

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

Вот эскиз, использующий типизированный, по умолчанию чистый, функциональный язык программирования Haskell . Мы создаем новые типы для ключевых концепций вашей проблемы и разбиваем работу на отдельные функции, которые выполняют одну задачу за раз. IO и другие побочные эффекты (например, создание символической ссылки) ограничены определенными функциями и обозначены типом. Чтобы различать два режима работы, мы используем тип суммы .

--
-- User can request an image or a thumbnail of the image.
-- If the user requests the thumbnail of the image, and it doesn't yet exist, create it using
-- python's PIL module. Also create a symbolic link to the original or
-- thumbnail with a human readable path, because the original image name is a
-- hashcode, and not descriptive of it's contents. Finally, redirect to the
-- symbolic link of that image.
--

module ImageEvent where

import System.FilePath
import System.Posix.Files

-- Request types
data ImgRequest = Thumb ImgName | Full ImgName

-- Hash of image 
type ImgName = String

-- Type of redirects
data Redirect

request :: ImgRequest -> IO Redirect
request (Thumb img) = do
    f <- createThumbnail img
    let f' = normalizePath f
    createSymbolicLink f f'
    return (urlOf f)

request (Full img)  = do
    createSymbolicLink f f'
    return (urlOf f)
    where
        f  = lookupPath img
        f' = normalizePath f

Вместе с некоторыми помощниками, которые я оставлю на ваше усмотрение.

-- Creates a thumbnail for a given image at a path, returns new filepath
createThumbnail :: ImgName -> IO FilePath
createThumbnail f = undefined
    where
        p = lookupPath f

-- Create absolute path from image hash
lookupPath :: ImgName -> FilePath
lookupPath f = "/path/to/img" </> f <.> "png"

-- Given an image, construct a redirect to that image url
urlOf :: FilePath -> Redirect
urlOf = undefined

-- Compute human-readable path from has
normalizePath :: FilePath -> FilePath
normalizePath = undefined

Действительно красивое решение будет абстрагировать модель запроса / ответа со структурой данных, чтобы представить последовательность команд, которые должны быть выполнены. Запрос приходит, строит структуру исключительно для представления того, какая работа ему нужна, и это передается в механизм исполнения, который выполняет такие вещи, как создание файлов и так далее. Тогда основная логика будет полностью чистыми функциями (не то, чтобы в этой задаче было много основной логики). В качестве примера такого стиля действительно чисто функционального программирования с эффектами я рекомендую статью Вутера Свистры, `` Красота в чудовище: функциональная семантика для неуклюжего отряда ''

5 голосов
/ 31 мая 2011

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

Я хотел работать над личным проектом, который требовал параллелизма. Я оглянулся и нашел Эрланг. Я выбрал его, потому что думал, что он лучше всего поддерживает параллелизм, а не по какой-либо другой причине. Я никогда раньше не работал с функциональным языком (и просто для сравнения я начал заниматься объектно-ориентированным программированием в начале 1990-х годов).

Я прочитал книгу Эрстронга Армстронга. Это было тяжело. У меня был небольшой проект, над которым я работал, но я продолжал над ним работать.

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

Я прошел через этап, когда я сопоставил объекты с процессами erlang, но это было не слишком долго, и я вышел из этого.

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

Я думаю, что работа в Python может сдерживать вас, и я настоятельно рекомендую вам проверить erlang. Его синтаксис очень чужд - но это также хорошо, так как более традиционный синтаксис (по крайней мере для меня) приведет к попытке запрограммировать его старыми способами и разочароваться.

4 голосов
/ 31 мая 2011

Лично я думаю, что проблема в том, что вы пытаетесь использовать функциональное программирование для решения задач, которые разработаны / заявлены для императивного программирования.3 популярные парадигмы (функциональная, императивная, объектно-ориентированная) имеют разные сильные стороны:

  • Функциональное программирование делает упор на описании ЧТО должно быть сделано, обычно в терминах ввода / результата.
  • Императивное программирование подчеркивает, КАК что-то делать, обычно в терминах списка и порядка предпринимаемых шагов, и состояния для изменения.
  • Объектно-ориентированное программирование делает акцент на ОТНОШЕНИЯХ между сущностями в системе

Таким образом, когда вы подходите к проблеме, первым делом стоит перефразировать ее так, чтобы предполагаемая парадигма могла правильно ее решить.Кстати, в качестве бокового узла, насколько я знаю, не существует понятия «чистый ООП».Код в методах ваших классов ООП (будь то Java, C #, C ++, Python или Objective C) является обязательным.

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

Чтобы переформулировать проблему:

  • Ввод: Изображениетип (Полный или миниатюра), Имя изображения, файловая система
  • Вывод: запрошенное изображение, файловая система с запрошенным изображением

Из новой постановки задачи вы можете решить еенапример:

def requestImage(type, name, fs) : 
    if type == "full" :
        return lookupImage(name, fs), fs
    else:
        thumb = lookupThumb(name, fs)
        if(thumb) :
            return thumb, fs
        else:
            thumb = createThumbnail(lookupImage(name, fs))
            return thumb, addThumbnailToFs(fs, name, thumb)

Конечно, это неполно, но мы всегда можем рекурсивно решить lookupImage, lookupThumb, createThumbnail и addThumbnailToFs примерно одинаково.

Большое примечание: создание новогоФайловая система звучит большой, но это не должно быть.Например, если это модуль на более крупном веб-сервере, «новая файловая система» может быть такой же простой, как инструкция, указывающая, где должна быть новая миниатюра.Или, в худшем случае, это может быть монада ввода-вывода, чтобы поместить миниатюру в соответствующее место.

...