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

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

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

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

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

26
задан Frank Henard 1 June 2011 в 12:17
поделиться

2 ответа

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

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

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

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

Для повторного решения проблемы:

  • Входные данные: тип изображения (полное или уменьшенное изображение), имя изображения, файловая система
  • Выходные данные: запрошенное изображение, файловая система с запрошенным image

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

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 примерно таким же образом.

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

4
ответ дан 28 November 2019 в 07:51
поделиться

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

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

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

20
ответ дан 28 November 2019 в 07:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: