Stefan Witzel
Stefan Witzel

Reputation: 343

Reordering type parameters in Haskell

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.

  1. How would I turn them into functors in their second variable?
  2. How would I turn actual tuples (not my new type) into a functor?

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

Answers (3)

chepner
chepner

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

parkerbrads
parkerbrads

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

amalloy
amalloy

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

Related Questions