Аппликатив без функтора - PullRequest
       11

Аппликатив без функтора

10 голосов
/ 11 августа 2011

У меня есть тип Image, который в основном является массивом с плавающей точкой. Легко создавать функции такие как map :: (Float -> Float) -> Image -> Image или zipWith :: (Float -> Float -> Float) -> Image -> Image -> Image.

Тем не менее, у меня есть ощущение, что было бы также возможно предоставить нечто похожее на аппликативный экземпляр поверх этих функций, позволяя более гибкие манипуляции на уровне пикселей, например ((+) <$> image1 <*> image2) или ((\x y z -> (x+y)/z) <$> i1 <*> i2 <*> i3). Однако наивный подход терпит неудачу, поскольку тип Image не может содержать ничего, кроме плавающих элементов, что делает невозможным реализацию fmap как таковую.

Как это можно реализовать?

Ответы [ 3 ]

15 голосов
/ 11 августа 2011

Читая комментарии, я немного волнуюсь, что размер здесь под ковром.Есть ли разумное поведение при несовпадении размеров?

Между тем, может быть что-то, что вы можете сделать разумно по следующим направлениям.Даже если ваши массивы нелегко сделать полиморфными, вы можете сделать экземпляр Applicative подобным этому.

data ArrayLike x = MkAL {sizeOf :: Int, eltOf :: Int -> x}

instance Applicative ArrayLike where
  pure x                 = MkAL maxBound  (pure x)
  MkAL i f <*> MkAL j g  = MkAL (min i j) (f <*> g)

(Энтузиасты заметят, что я взял продукт аппликатива (Int ->) сэто вызвано (maxBound, min) моноидом.)

Не могли бы вы сделать чистое соответствие

imAL :: Image -> ArrayLike Float
alIm :: ArrayLike Float -> Image

с помощью проекции и табулирования?Если это так, вы можете написать такой код:

alIm $ (f <$> imAL a1 <*> ... <*> imAL an)

Более того, если вы хотите обернуть этот шаблон как перегруженный оператор,

imapp :: (Float -> ... -> Float) -> (Image -> ... -> Image)

это стандартное упражнение в классе типовпрограммирование!(Спросите, нужна ли вам дополнительная подсказка.)

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

6 голосов
/ 15 августа 2011

Вы получите гораздо более композиционный тип Image, если обобщить тип «пиксель» из Float и перейти от конечной и дискретной областей (массивов) к бесконечной и непрерывной области.В качестве демонстрации этих обобщений см. Статью Функциональные изображения и соответствующую галерею (конечных выборок) примеров изображений .В результате вы получите экземпляры Monoid, Functor, Applicative, Monad и Comonad.Кроме того, значения этих экземпляров полностью определяются соответствующими экземплярами для функций, удовлетворяющих принципу семантических морфизмов классов типов , как описано в статье денотационный дизайн с морфизмами классов типов .Раздел 13.2 этого документа кратко описывает образы.

6 голосов
/ 11 августа 2011

Как вы ожидаете выполнять операции с пикселями на изображении? То есть для ((+) <$> image1 <*> image2) хотите ли вы выполнить все операции в Haskell и создать новое полученное изображение, или вам придется вызывать функции C, чтобы выполнить всю обработку?

Если это первый, ответ свиновода - это подход, который я бы выбрал.

Если вместо этого требуется, чтобы все манипуляции с изображениями обрабатывались через C, как насчет создания небольшого DSL для представления операций?

...