Johnny
Johnny

Reputation: 447

How can I instantiate Functor for this data type?

How Can I instantiate the following data types to be Functor ?

data LiftItOut f a = LiftItOut (f a)
data Parappa f g a = DaWrappa (f a) (g a)
data IgnoreOne f g a b = IgnoringSomething (f a) (g b)
data Notorious g o a t = Notorious (g o) (g a) (g t)

There are not very clear for the declaration themselves, inside the parantheses in the right member, is that function application (I ve never seen that, only basic type constructors)? I am new to haskell and I am just trying to understand the basics.

Upvotes: 1

Views: 185

Answers (2)

Will Ness
Will Ness

Reputation: 71119

A functor f is a type constructor with an associated function fmap that from a function of type (a -> b) creates a function of type (f a) -> (f b) which applies it "on the inside": (the parentheses are redundant and are used for clarity/emphasis only)

fmap :: (Functor f) => (  a  ->    b) 
                    -> (f a) -> (f b)       

-- i.e.           g ::    a  ->    b        -- from this
--           --------------------------
--           fmap g :: (f a) -> (f b)       -- we get this

(read it "fmap of g from a to b goes from f a to f b").

Put differently, something being a "Functor" means that it can be substituted for f in

fmap id (x :: f a)  =  x
(fmap g . fmap h)   =  fmap (g . h)

so that the expressions involved make sense (i.e. are well formed, i.e. have a type), and, importantly, the above equations hold -- they are in fact the two "Functor laws".


You have

data  LiftItOut h a  =  MkLiftItOut    (h a)       -- "Mk..." for "Make..."
      -------------     -----------    ------
      new type,         data           type of the data constructor's
      defined here       constructor    one argument (one field)

This means h a is a type of a thing which can serve as an argument to MkLiftItOut. For example, Maybe Int (i.e. h ~ Maybe and a ~ Int), [(Float,String)] (i.e. h ~ [] and a ~ (Float,String)), etc.

h, a are type variables -- meaning, they can be replaced by any specific type so that the whole syntactic expressions make sense.

These syntactic expressions include MkLiftItOut x which is a thing of type LiftItOut h a provided x is a thing of type h a; LiftItOut h a which is a type; h a which is a type of a thing which can appear as an argument to MkLiftItOut. Thus we can have in our programs

v1 = MkLiftItOut ([1,2,3]   :: []    Int   )  ::  LiftItOut  []    Int
v2 = MkLiftItOut ((Just "") :: Maybe String)  ::  LiftItOut  Maybe String
v3 = MkLiftItOut (Nothing   :: Maybe ()    )  ::  LiftItOut  Maybe ()
.....

etc. Then we have

ghci> :i Functor
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a
..........

This means that Functor f => (f a) is a type of a thing which a variable can reference, e.g.

                 -- f        a
v4  = Just 4     :: Maybe   Int
v41 =      4     ::         Int

v5  = [4.4, 5.5] :: []      Float
v51 =  4.4       ::         Float
v52 =       5.5  ::         Float

v6  = (1,"a")    :: ((,) Int)  String  -- or simpler, `(Int, String)`
v61 =    "a"     ::            String

v7  = (\x -> 7)  :: ((->) Int) Int     -- or simpler, `Int -> Int`

Here a is a type of a thing, f a is a type of a thing, f is a type which, when given a type of a thing, becomes a type of a thing; etc. There's no thing which can be referenced by a variable which would have the type f on its own.

All the above fs are instances of the Functor typeclass. This means that somewhere in the libraries there are definitions of

instance Functor Maybe   where ....
instance Functor []      where ....
instance Functor ((,) a)  where ....
instance Functor ((->) r) where ....

Notice we always have the f, and the a. f in particular can be made of more than one constituents, but a is always some one type.

Thus in this case we must have

instance Functor (LiftItOut h) where ....

(...why? do convince yourself in this; see how all the above statements apply and are correct)

Then the actual definition must be

   -- fmap :: (a -> b) -> f           a -> f           b
   -- fmap :: (a -> b) -> LiftItOut h a -> LiftItOut h b
   fmap       g       (MkLiftItOut  x )  =  (MkLiftItOut  y )
     where
     y = ....

In particular, we'll have

    --     g :: a -> b     --   x :: (h a)     --   y :: (h b)

and we don't even know what the h is.

How can we solve this? How can we construct an h b-type of thing from an h a-type of thing when we don't even know anything about h, a, nor b?

We can't.

But what if we knew that h is also a Functor?

instance (Functor h) => Functor (LiftItOut h) where
   -- fmap :: (a -> b) -> (f           a) -> (f           b)
   -- fmap :: (a -> b) -> (LiftItOut h a) -> (LiftItOut h b)
   fmap       g       (MkLiftItOut  x )  =  (MkLiftItOut  y )
     where
     -- fmap :: (a -> b) -> (h a) -> (h b)
     y = ....

Hopefully you can finish this up. And also do the other types in your question as well. If not, post a new question for the one type with which you might have any further problems.

Upvotes: 0

Ask the compiler to show you how. Use the command line flag -ddump-deriv, enable the DeriveFunctor language extension, and put deriving Functor at the end of each type definition, and then the compiler will print Functor instances for each of them:

==================== Derived instances ====================
Derived class instances:
  instance GHC.Base.Functor g =>
           GHC.Base.Functor (Main.Notorious g o a) where
    GHC.Base.fmap f_aK1 (Main.Notorious a1_aK2 a2_aK3 a3_aK4)
      = Main.Notorious a1_aK2 a2_aK3 (GHC.Base.fmap f_aK1 a3_aK4)
    (GHC.Base.<$) z_aK5 (Main.Notorious a1_aK6 a2_aK7 a3_aK8)
      = Main.Notorious a1_aK6 a2_aK7 ((GHC.Base.<$) z_aK5 a3_aK8)
  
  instance forall k (f :: k -> *) (g :: * -> *) (a :: k).
           GHC.Base.Functor g =>
           GHC.Base.Functor (Main.IgnoreOne f g a) where
    GHC.Base.fmap f_aK9 (Main.IgnoringSomething a1_aKa a2_aKb)
      = Main.IgnoringSomething a1_aKa (GHC.Base.fmap f_aK9 a2_aKb)
    (GHC.Base.<$) z_aKc (Main.IgnoringSomething a1_aKd a2_aKe)
      = Main.IgnoringSomething a1_aKd ((GHC.Base.<$) z_aKc a2_aKe)
  
  instance (GHC.Base.Functor f, GHC.Base.Functor g) =>
           GHC.Base.Functor (Main.Parappa f g) where
    GHC.Base.fmap f_aKf (Main.DaWrappa a1_aKg a2_aKh)
      = Main.DaWrappa
          (GHC.Base.fmap f_aKf a1_aKg) (GHC.Base.fmap f_aKf a2_aKh)
    (GHC.Base.<$) z_aKi (Main.DaWrappa a1_aKj a2_aKk)
      = Main.DaWrappa
          ((GHC.Base.<$) z_aKi a1_aKj) ((GHC.Base.<$) z_aKi a2_aKk)
  
  instance GHC.Base.Functor f =>
           GHC.Base.Functor (Main.LiftItOut f) where
    GHC.Base.fmap f_aKl (Main.LiftItOut a1_aKm)
      = Main.LiftItOut (GHC.Base.fmap f_aKl a1_aKm)
    (GHC.Base.<$) z_aKn (Main.LiftItOut a1_aKo)
      = Main.LiftItOut ((GHC.Base.<$) z_aKn a1_aKo)

That's kind of messy-looking, but it's rather straightforward to clean up:

data LiftItOut f a = LiftItOut (f a)
instance Functor f => Functor (LiftItOut f) where
    fmap f (LiftItOut a) = LiftItOut (fmap f a)

data Parappa f g a = DaWrappa (f a) (g a)
instance (Functor f, Functor g) => Functor (Parappa f g) where
    fmap f (DaWrappa a1 a2) = DaWrappa (fmap f a1) (fmap f a2)

data IgnoreOne f g a b = IgnoringSomething (f a) (g b)
instance Functor g => Functor (IgnoreOne f g a) where
    fmap f (IgnoringSomething a1 a2) = IgnoringSomething a1 (fmap f a2)

data Notorious g o a t = Notorious (g o) (g a) (g t)
instance Functor g => Functor (Notorious g o a) where
    fmap f (Notorious a1 a2 a3) = Notorious a1 a2 (fmap f a3)

Also worth noting that your LiftItOut is isomorphic to Ap and IdentityT, and your Parappa is isomorphic to Product.

Upvotes: 4

Related Questions