Rumca
Rumca

Reputation: 1829

What does Functor's fmap tell about types?

What does f a and f b tell me about its type?

class Functor f where
  fmap :: (a -> b) -> f a -> f b

I think I get the idea behind standard instances of a functor. However I'm having hard time understanding what f a and f actually represent.

I understand that f a and f b are just types and they must carry information what type constructor was used to create them and type arguments that were used.

Is f a type constructor of kind * -> *? Is (->) r a type constructor just like Maybe is?

Upvotes: 2

Views: 190

Answers (2)

Luis Casillas
Luis Casillas

Reputation: 30237

My (possibly mildly tortured) reading of chapter 4 of the Haskell 2010 Report is that Maybe and (->) r are both types, of kind * -> *. Alternatively, the Report also labels them as type expressions—but I can't discern a firm difference in how the Report uses the two terms, except perhaps for surface syntax details. (->) and Maybe are type constructors; type expressions are assembled from type constructors and type variables.

For example, section 4.1.1 ("Kinds") of the 2010 report says (my boldface):

To ensure that they are valid, type expressions are classified into different kinds, which take one of two possible forms:

  • The symbol ∗ represents the kind of all nullary type constructors.
  • If κ1 and κ2 are kinds, then κ1 → κ2 is the kind of types that take a type of kind κ1 and return a type of kind κ2.

Section 4.3.2, "Instance Declarations" (my boldface):

An instance declaration that makes the type T to be an instance of class C is called a C-T instance declaration and is subject to these static restrictions:

  • A type may not be declared as an instance of a particular class more than once in the program.
  • The class and type must have the same kind; this can be determined using kind inference as described in Section 4.6.

So going by that language, the following instance declaration makes the type (->) r to be an instance of the class Functor:

instance Functor ((->) r) where
    fmap f g = f . g

The funny thing about this terminology is that we call (->) r a "type" even though there are no expressions in Haskell that have that type—not even undefined:

foo :: (->) r
foo = undefined

{-
[1 of 1] Compiling Main             ( ../src/scratch.hs, interpreted )

../src/scratch.hs:1:8:
    Expecting one more argument to `(->) r'
    In the type signature for `foo': foo :: (->) r
-}

But I think that's not a big deal. Basically, all declarations in Haskell must have types of kind *.

As a side note, from my limited understanding of dependently typed languages, many of these lack Haskell's firm distinction between terms and types, so that something like (->) Boolean is an expression whose value is a function that takes a type as its argument and produces a type as its result.

Upvotes: 1

not my job
not my job

Reputation: 672

I understand that f a and f b are just types and they must carry information what type constructor was used to create them and type arguments that were used.

Good explanation.

Is f a type constructor of kind * -> *?

In effect.

Is (->) r a type constructor just like Maybe is?

In effect, yes:

Yes in the sense that you can apply it to a type like String and get r -> String, just like you can apply Maybe to String to get Maybe String. You can use for f anything that gives you a type from any other type.

..but no...

No, in the sense that Daniel Wagner points out; To be precise, Maybe and [] are type constructors, but (->) r and Either a are sort of like partially applied type constructors. Nevertheless they make good functors, because you can freely apply functions "inside" them and change the type of "the contents".

(Stuff in inverted commas is very hand-wavy imprecise terminology.)

Upvotes: 2

Related Questions