Thinking about haskell functors in .net

I’ve been teaching myself haskell lately and came across an interesting language feature called functors. Functors are a way of describing a transformation when you have a boxed container. They have a generic signature of

('a -> 'b) -> f 'a -> f 'b

Where f isn’t a “function”, it’s a type that contains the type of 'a.

The idea is you can write custom map functions for types that act as generic containers. Generic containers are things like lists, an option type, or other things that hold something. By itself a list is nothing, it has to be a list OF something. Not to get sidetracked too much, but these kinds of boxes are called Monads.

Anyways, let’s do this in C# by assuming that we have a box type that holds something.

public class Box<T>
    public T Data { get; set; }   

var boxes = new List<Box<string>>();

IEnumerable<string> boxNames  = boxes.Select(box => box.Data);

We have a type Box and a list of boxes. Then we Select (or map) a box’s inner data into another list. We could extract the projection into a separate function too:

public string BoxString(Box<string> p)
    return p.Data;

The type signature of this function is

Box-> string

But wouldn’t it be nice to be able to do work on a boxes data without having to explicity project it out? Like, maybe define a way so that if you pass in a box, and a function that works on a string, it’ll automatically unbox the data and apply the function to its data.

For example something like this (but this won’t compile obviously)

public String AddExclamation(String input){
   return input + "!";

IEnumerable<Box<string>> boxes = new List<Box<string>>();

IEnumerable<string> boxStringsExclamation = boxes.Select(AddExclamation);

In C# we have to add the projection step (which in this case is overloaded):

public String AddExclamation(Box<String> p){
   return AddExclamation(p.Data);

In F# you have to do basically the same thing:

type Box<'T> = { Data: 'T }

let boxes = List.init 10 (fun i -> { Data= i.ToString() })

let boxStrings = (fun i -> i.Data) boxes

But in Haskell, you can define this projection as part of the type by saying it is an instance of the Functor type class. When you make a generic type an instance of the functor type class you can define how maps work on the insides of that class.

data Box a = Data a deriving (Show)

instance Functor Box where
    fmap f (Data inside) = Data(f inside)    

main =
    print $ fmap (++"... your name!") (Data "my name")

This outputs

Data "my name... your name!"

Here I have a box that contains a value, and it has a value. Then I can define how a box behaves when someone maps over it. As long as the type of the box contents matches the type of the projection, the call to fmap works.

One comment

  1. Pingback: A functor is not a box | 神刀安全网

Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>