Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

Здесь представлен эскиз с использованием типичного по умолчанию чистого функционального языка программирования 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 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 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

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

Таким образом, когда вы приближаетесь к проблеме, первый порядок ведения бизнеса состоит в том, чтобы перефразировать ее так, чтобы предполагаемая парадигма могла ее правильно решить. Кстати, как сторона node, как я знаю, нет такой вещи, как "чистый ООП". Код в методах ваших классов ООП (будь то Java, С#, С++, 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 и addThumbnailToF примерно одинаково.

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