tero
tero

Reputation: 153

Haskell: functor from this data type?

Sorry about bad title. I have a problematic data type, which I'm trying to define as instance of functor.

So basically, what I have is something, which has

sample_logp :: s a -> a

and it should be transformed using

(a -> b)

to

sample_logp :: s b -> b

. The following code does not quite accomplish this, and succeeds only in

sample_logp :: s a -> b  

.

data Model s a = Model {
  sample_logp :: s a -> a
}

instance Functor (Model s) where
  fmap f m = Model {
    sample_logp = sample_logp'
  } where sample_logp' x = (f . (sample_logp m)) x

Is what I'm trying even possible? If so, how could this code be updated to achieve this?

Upvotes: 3

Views: 121

Answers (1)

Carl
Carl

Reputation: 27023

The standard approach here is to add more type variables.

data Model s a b = Model {
  sample_logp :: s a -> b
}

Once you have split the type variables, you have access to more tools. The Profunctor class is appropriate here. (Not typechecked since I don't have ghc on this system - comment or just fix it if my implementation is off.)

instance (Functor s) => Profunctor (Model s) where
    dimap f g (Model h) = Model $ g . h . fmap f
    lmap f (Model h) = Model $ h . fmap f
    rmap g (Model h) = Model $ g . h

Now, given that you have a Model s a a, which is the equivalent of your Model s a, you can convert it to a Model s b b by using dimap bToA aToB.

As the comments say, your original data type is invariant because it uses the same type variable in positive and negative positions. This means you need to supply conversion functions in each direction. Adding an extra type variable lets you take advantage of existing tools for doing that, like Profunctor.


Note that all of the above is based on the assumption that you are using covariant types for s. If s is contravariant, then you can write a direct Functor instance for your original type, as chi's comment says. That's a much less common situation, though.

Upvotes: 9

Related Questions