Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using an 'fmap-like' function on types of kind *

Say we have a data type defined as

data A a = A' a deriving Show 

We have

A :: * -> *

We can then make A an instance of Functor:

instance Functor A where fmap f (A' x) = A' (f x)

This allows us to make a value of type A and use fmap to apply a function to the wrapped value

Prelude> let x = A' 1
Prelude> fmap (+1) x
A' 2

Now, if we defined A as

data A = A' Int deriving Show

The kind of A is

A :: *

Consequently, we can not make A an instance of Functor -

instance Functor A where fmap f (A' x) = A' (f x)

<interactive>:4:18:
    Kind mis-match
    The first argument of `Functor' should have kind `* -> *',
    but `A' has kind `*'
    In the instance declaration for `Functor A'

My question is, is there a generic way to apply a function to the wrap value for data types of kind * with a data constructor that only takes one parameter (i.e., something analogous to fmap)? It's fairly easy to write custom function:

Prelude> let myFmap f (A' x) = A' (f x)
Prelude> let x = A' 1
Prelude> myFmap (+1) x
A' 2

It's also possible to make A an instance of Num:

instance Num A where (+) (A' x) (A' y) = A' (x + y) -- Ignoring the warnings
Prelude> A' 1 + A' 1 
A' 2

Both of these approaches work but is there a better way that I am missing?

like image 640
Michael T Avatar asked Dec 24 '22 13:12

Michael T


1 Answers

I don't think there's a good generic solution for the second half of the question, but I can at least answer the first half.

This is the purpose of the mono-traversable package, which provides a MonoFunctor typeclass. The instance for A' would be:

type instance Element A' = Int

instance MonoFunctor a' where
  omap f (A' i) = A' (f i)

YMMV on how useful this is versus a simple a'map :: (Int -> Int) -> A' -> A' function or a lens.

like image 179
Rein Henrichs Avatar answered Jan 02 '23 20:01

Rein Henrichs