Reputation: 343
I have a question about type parameters that I think is best expressed by an example. This piece of code
newtype Triple a b c = T (a,b,c)
instance Functor (Triple a b) where
fmap f (T (x, y, z)) = T (x, y, (f z))
expresses triples as functors in their third variable.
The general question is: suppose I have a parametric type m a b c d e
how do I express the parametric type m a b d e
obtained by fixing one parameter? Or equivalently, how do I express the parametric type m a b d e c
obtained by making an arbitrary parameter the last one?
Edit: it may not have become quite clear what I mean, so I'm trying to clarify: Triple
has kind * -> * -> * -> *
. So I can partially evaluate at two types to get something of kind * -> *
which could be Functor
or some other parametrized class. This evaluation is easy to do at the first two parameters but it is in principle possible at any two of the parameters, and I am asking how it can be done. This is essentially asking for a flip
on the level of types.
As a concrete use case I can have three parametrized classes Functor, Foo, and Bar, and I want (Triple _ b c) to be a Functor, (Triple a _ c) to be a Foo, and (Triple a b _) to be a Bar (for all a, b, c). So then Triple a b c
would be a Functor
, a Foo
and a Bar
. You would think of writing these one-parameter types a -> Triple a b c
, b -> Triple a b c
and c -> Triple a b c
but of course this literal notation expresses mapping types.
Edit2: Before posting a question on stackoverflow I always try to strip it to its abstract core, but this seems to obscure what I actually want. So a concrete variant of this question can now be found here.
Upvotes: 3
Views: 215
Reputation: 530940
A functor has kind Type -> Type
, so Triple :: Type -> Type -> Type -> Type
itself is not a functor; only the nearly-saturated partial application Triple a b
for 2 types a
and b
can be a functor.
Triple
is, however, an example of a "trifunctor", which you can define yourself.
class Trifunctor p where
trimap :: (a -> x) -> (b -> y) -> (c -> z) -> p a b c -> p x y z
-- There are only so many synonyms for first, second, etc
map13 :: (a -> x) -> p a b c -> p x y z
map13 f = trimap f id id
map23 :: (b -> y) -> p a b c -> p x y z
map23 f = trimap id f id
map33 :: (c -> z) -> p a b c -> p x y z
map33 f = trimap id id f
instance Trifunctor Triple where
trimap f g h (Triple x y z) = Triple (f x) (g y) (h z)
The pattern generalizes; the product of n
types is an n
-functor.
Upvotes: 1
Reputation: 116
In this specific case you might get what you need by using lenses.
The combination of over and all the functions in the tuple module (_1, _2, _3 etc.) gives you the ability to lift functions into more tuple positions than just the rightmost one.
EDIT Adding an example.
So say we have this tuple.
(1, "Foo", True)
And we want to (+ 1)
to the value in its first position.
> import Control.Lens (over, _1)
> over _1 (+ 1) (1, "Foo", True)
(2,"Foo",True)
Or upper-case the string in its second position
> import Data.Char (toUpper)
> import Control.Lens (over, _2)
> over _2 (map toUpper) (1, "Foo", True)
(1,"FOO",True)
Or perhaps we want to flip the bool in its third position
> import Control.Lens (over, _3)
> over _3 not (1, "Foo", True)
(1,"Foo",False)
Upvotes: 3
Reputation: 91857
This is what newtypes are for. You wrap an existing type up in a newtype, letting you do different stuff to it at the type level while leaving the value level unchanged. For example:
newtype SecondTriple a b c = SecondTriple (a, c, b)
instance Functor (SecondTriple a b) where
fmap f (SecondTriple (x, z, y)) = SecondTriple (x, f z, y)
If you like, you can wrap Triple instead of wrapping (,,)
, but of course you can't use Triple's Functor instance anyway so it's not much help.
Upvotes: 3