I'm developing a class representing key/value mappings, and I've got a function which is basically like alterF
:
class C t where
...
alterF :: Functor f =>
(Maybe (Value t) -> f (Maybe (Value t))) -> Key t -> t -> f t
Unfortunately, this breaks GeneralisedNewtypeDeriving
. In some cases, this is reasonable, as GeneralisedNewtypeDeriving
from what I understand essentially uses Coercible and the function coerce
. Coercible
represents types which are representationally equal, i.e. they have the same representation at run time, so we can convert between them for free. For example, given:
newtype T a = T a
we have:
Coercible a (T a)
Coercible (T a) a
but we don't have (in general):
Coercible (f a) (f (T a))
Coercible (f (T a)) (f a)
For example, GADTs violate this representational equality. But there are lots of values of f
that do work. For example:
Coercible (Maybe a) (Maybe (T a))
Coercible (Maybe (T a)) (Maybe a)
Coercible [a] [T a]
Coercible [T a] [a]
Coercible (Identity a) (Identity (T a))
Coercible (Identity (T a)) (Identity a)
It also occurs to me that this instance could be written:
Functor f => Coercible (f a) (f (T a))
Functor f => Coercible (f (T a)) (f a)
Just using fmap
. Unlike the usual coerce
, this wouldn't be free at runtime, but it will work.
So I've got a class with 10 functions, 9 of which work fine with GeneralisedNewtypeDeriving
. There's just this final one which doesn't, which could be resolved mechanically using fmap
. Do I have to write custom wrapping/unwrapping implementations for all my class functions, or is there a way to either require me to write the implementation for just the problem function or alternatively coax GHC into using fmap
as part of it's GeneralisedNewtypeDeriving
?
To add new functionality to a derived class, simply declare that functionality in the derived class like normal: Now the public will be able to call getValue () on an object of type Derived to access the value of m_value. This produces the result:
What a mess. This is where functors come into use. A functor (or function object) is a C++ class that acts like a function. Functors are called using the same old function call syntax.
One obvious answer might be global variables. However, good coding practices do not advocate the use of global variables and say they must be used only when there is no other alternative. Functors are objects that can be treated as though they are a function or function pointer.
Although it may be obvious, objects of type Base have no access to the getValue () function in Derived. The following does not work: This is because there is no getValue () function in Base. Function getValue () belongs to Derived. Because Derived is a Base, Derived has access to stuff in Base.
If f
is a Functor
, you can make a "representational wrapper" for it
data Rep f a where
Rep :: (b -> a) -> f b -> Rep f a
which is isomorphic to f
except that it is representational in a
, by what is essentially existential quantification over any nominal variance f
might have. I think this construction happens to have some fancy category theory name but I don't remember what it is. To get the f a
back out of a Rep f a
, you need to use f
's Functor
hood.
You can use this wrapper in your method, ensuring that your class varies representationally.
alterFRep :: (Functor f)
=> (Maybe (Value t) -> Rep f (Maybe (Value t))) -> Key t -> t -> Rep f t
And then make the real "method" just a regular function in terms of it by using the isomorphism with Rep f
. You can also make a convenience method for instance authors:
toAlterFRep ::
(forall f t. (Functor f) => (Maybe (Value t) -> f (Maybe (Value t))) -> Key t -> t -> f t)
-> (forall f t. (Functor f) => (Maybe (Value t) -> Rep f (Maybe (Value t))) -> Key t -> t -> Rep f t)
so they don't have to worry about what the heck Rep
is, they just implement alterF
normally and use toAlterFRep
on it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With