Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applicative without a functor

I have a type Image which is basically an c-array of floats. It is easy to create functions such as map :: (Float -> Float) -> Image -> Image, or zipWith :: (Float -> Float -> Float) -> Image -> Image -> Image.

However, I have a feeling that it would also be possible to provide something that looks like an applicative instance on top of these functions, allowing more flexible pixel level manipulations like ((+) <$> image1 <*> image2) or ((\x y z -> (x+y)/z) <$> i1 <*> i2 <*> i3). However, the naive approach fails, since Image type cannot contain things other than floats, making it impossible to implement fmap as such.

How could this be implemented?

like image 437
aleator Avatar asked Aug 11 '11 11:08

aleator


People also ask

Is applicative a functor?

Applicative functors are the programming equivalent of lax monoidal functors with tensorial strength in category theory. Applicative functors were introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.

Is every monad a functor?

Monads are not a replacement for applicative functors Instead, every monad is an applicative functor (as well as a functor).

Why is functor useful?

Functors are very useful in modeling functional effects to apply a function to computations that did not yet finish. Functors form a base for more complex abstractions like Applicative, Monad, Comonad.

Is Fmap a functor?

The expression fmap (*2) is a function that takes a functor f over numbers and returns a functor over numbers. That functor can be a list, a Maybe , an Either String, whatever. The expression fmap (replicate 3) will take a functor over any type and return a functor over a list of elements of that type.


1 Answers

Reading the comments, I'm a little worried that size is under the carpet here. Is there a sensible behaviour when sizes mismatch?

Meanwhile, there may be something you can sensibly do along the following lines. Even if your arrays aren't easy to make polymorphic, you can make an Applicative instance like this.

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)

(Enthusiasts will note that I've taken the product of the (Int ->) applicative with that induced by the (maxBound, min) monoid.)

Could you make a clean correspondence

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

by projection and tabulation? If so, you can write code like this.

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

Moreover, if you then want to wrap that pattern up as an overloaded operator,

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

it's a standard exercise in typeclass programming! (Ask if you need more of a hint.)

The crucial point, though, is that the wrapping strategy means you don't need to monkey with your array structures in order to put functional superstructure on top.

like image 191
pigworker Avatar answered Nov 04 '22 15:11

pigworker