I\'ve come across the term \'Functor\' a few times while reading various articles on functional programming, but the authors typically assume the reader already understands
In a comment to the top-voted answer, user Wei Hu asks:
I understand both ML-functors and Haskell-functors, but lack the insight to relate them together. What's the relationship between these two, in a category-theoretical sense?
Note: I don't know ML, so please forgive and correct any related mistakes.
Let's initially assume that we are all familiar with the definitions of 'category' and 'functor'.
A compact answer would be that "Haskell-functors" are (endo-)functors F : Hask -> Hask
while "ML-functors" are functors G : ML -> ML'
.
Here, Hask
is the category formed by Haskell types and functions between them, and similarly ML
and ML'
are categories defined by ML structures.
Note: There are some technical issues with making Hask
a category, but there are ways around them.
From a category theoretic perspective, this means that a Hask
-functor is a map F
of Haskell types:
data F a = ...
along with a map fmap
of Haskell functions:
instance Functor F where
fmap f = ...
ML is pretty much the same, though there is no canonical fmap
abstraction I am aware of, so let's define one:
signature FUNCTOR = sig
type 'a f
val fmap: 'a -> 'b -> 'a f -> 'b f
end
That is f
maps ML
-types and fmap
maps ML
-functions, so
functor StructB (StructA : SigA) :> FUNCTOR =
struct
fmap g = ...
...
end
is a functor F: StructA -> StructB
.
There are three different meanings, not much related!
In Ocaml it is a parametrized module. See manual. I think the best way to grok them is by example: (written quickly, might be buggy)
module type Order = sig
type t
val compare: t -> t -> bool
end;;
module Integers = struct
type t = int
let compare x y = x > y
end;;
module ReverseOrder = functor (X: Order) -> struct
type t = X.t
let compare x y = X.compare y x
end;;
(* We can order reversely *)
module K = ReverseOrder (Integers);;
Integers.compare 3 4;; (* this is false *)
K.compare 3 4;; (* this is true *)
module LexicographicOrder = functor (X: Order) ->
functor (Y: Order) -> struct
type t = X.t * Y.t
let compare (a,b) (c,d) = if X.compare a c then true
else if X.compare c a then false
else Y.compare b d
end;;
(* compare lexicographically *)
module X = LexicographicOrder (Integers) (Integers);;
X.compare (2,3) (4,5);;
module LinearSearch = functor (X: Order) -> struct
type t = X.t array
let find x k = 0 (* some boring code *)
end;;
module BinarySearch = functor (X: Order) -> struct
type t = X.t array
let find x k = 0 (* some boring code *)
end;;
(* linear search over arrays of integers *)
module LS = LinearSearch (Integers);;
LS.find [|1;2;3] 2;;
(* binary search over arrays of pairs of integers,
sorted lexicographically *)
module BS = BinarySearch (LexicographicOrder (Integers) (Integers));;
BS.find [|(2,3);(4,5)|] (2,3);;
You can now add quickly many possible orders, ways to form new orders, do a binary or linear search easily over them. Generic programming FTW.
In functional programming languages like Haskell, it means some type constructors (parametrized types like lists, sets) that can be "mapped". To be precise, a functor f
is equipped with (a -> b) -> (f a -> f b)
. This has origins in category theory. The Wikipedia article you linked to is this usage.
class Functor f where
fmap :: (a -> b) -> (f a -> f b)
instance Functor [] where -- lists are a functor
fmap = map
instance Functor Maybe where -- Maybe is option in Haskell
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
fmap (+1) [2,3,4] -- this is [3,4,5]
fmap (+1) (Just 5) -- this is Just 6
fmap (+1) Nothing -- this is Nothing
So, this is a special kind of a type constructors, and has little to do with functors in Ocaml!
In functional programming, a functor is essentially a construction of lifting ordinary unary functions (i.e. those with one argument) to functions between variables of new types. It is much easier to write and maintain simple functions between plain objects and use functors to lift them, then to manually write functions between complicated container objects. Further advantage is to write plain functions only once and then re-use them via different functors.
Examples of functors include arrays, "maybe" and "either" functors, futures (see e.g. https://github.com/Avaq/Fluture), and many others.
Consider the function constructing the full person's name from the first and last names. We could define it like fullName(firstName, lastName)
as function of two arguments, which however would not be suitable for functors that only deal with functions of one arguments. To remedy, we collect all the arguments in a single object name
, which now becomes the function's single argument:
// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName
Now what if we have many people in an array? Instead of manually go over the list, we can simply re-use our function fullName
via the map
method provided for arrays with short single line of code:
fullNameList = nameList => nameList.map(fullName)
and use it like
nameList = [
{firstName: 'Steve', lastName: 'Jobs'},
{firstName: 'Bill', lastName: 'Gates'}
]
fullNames = fullNameList(nameList)
// => ['Steve Jobs', 'Bill Gates']
That will work, whenever every entry in our nameList
is an object providing both firstName
and lastName
properties. But what if some objects don't (or even aren't objects at all)? To avoid the errors and make the code safer, we can wrap our objects into the Maybe
type (se e.g. https://sanctuary.js.org/#maybe-type):
// function to test name for validity
isValidName = name =>
(typeof name === 'object')
&& (typeof name.firstName === 'string')
&& (typeof name.lastName === 'string')
// wrap into the Maybe type
maybeName = name =>
isValidName(name) ? Just(name) : Nothing()
where Just(name)
is a container carrying only valid names and Nothing()
is the special value used for everything else. Now instead of interrupting (or forgetting) to check the validity of our arguments, we can simply reuse (lift) our original fullName
function with another single line of code, based again on the map
method, this time provided for the Maybe type:
// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)
and use it like
justSteve = maybeName(
{firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})
notSteve = maybeName(
{lastName: 'SomeJobs'}
) // => Nothing()
steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')
notSteveFN = maybeFullName(notSteve)
// => Nothing()
A Functor in Category Theory is a map between two categories respecting composition of their morphisms. In a Computer Language, the main Category of interest is the one whose objects are types (certain sets of values), and whose morphisms are functions f:a->b
from one type a
to another type b
.
For example, take a
to be the String
type, b
the Number type, and f
is the function mapping a string into its length:
// f :: String -> Number
f = str => str.length
Here a = String
represents the set of all strings and b = Number
the set of all numbers. In that sense, both a
and b
represent objects in the Set Category (which is closely related to the category of types, with the difference being inessential here). In the Set Category, morphisms between two sets are precisely all functions from the first set into the second. So our length function f
here is a morphism from the set of strings into the set of numbers.
As we only consider the set category, the relevant Functors from it into itself are maps sending objects to objects and morphisms to morphisms, that satisfy certain algebraic laws.
Array
Array
can mean many things, but only one thing is a Functor -- the type construct, mapping a type a
into the type [a]
of all arrays of type a
. For instance, the Array
functor maps the type String
into the type [String]
(the set of all arrays of strings of arbitrary length), and set type Number
into the corresponding type [Number]
(the set of all arrays of numbers).
It is important not to confuse the Functor map
Array :: a => [a]
with a morphism a -> [a]
. The functor simply maps (associates) the type a
into the type [a]
as one thing to another. That each type is actually a set of elements, is of no relevance here. In contrast, a morphism is an actual function between those sets. For instance, there is a natural morphism (function)
pure :: a -> [a]
pure = x => [x]
which sends a value into the 1-element array with that value as single entry. That function is not a part of the Array
Functor! From the point of view of this functor, pure
is just a function like any other, nothing special.
On the other hand, the Array
Functor has its second part -- the morphism part. Which maps a morphism f :: a -> b
into a morphism [f] :: [a] -> [b]
:
// a -> [a]
Array.map(f) = arr => arr.map(f)
Here arr
is any array of arbitrary length with values of type a
, and arr.map(f)
is the array of the same length with values of type b
, whose entries are results of applying f
to the entries of arr
. To make it a functor, the mathematical laws of mapping identity to identity and compositions to compositions must hold, which are easy to check in this Array
example.
The best answer to that question is found in "Typeclassopedia" by Brent Yorgey.
This issue of Monad Reader contain a precise definition of what a functor is as well as many definition of other concepts as well as a diagram. (Monoid, Applicative, Monad and other concept are explained and seen in relation to a functor).
http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf
excerpt from Typeclassopedia for Functor: "A simple intuition is that a Functor represents a “container” of some sort, along with the ability to apply a function uniformly to every element in the container"
But really the whole typeclassopedia is a highly recommended reading that is surprisingly easy. In a way you can see the typeclass presented there as a parallel to design pattern in object in the sense that they give you a vocabulary for given behavior or capability.
Cheers
Arrays in JavaScript implement map and are therefore functors. Promises, Streams and Trees often implement map in functional languages, and when they do, they are considered functors. The map method of the functor takes it’s own contents and transforms each of them using the transformation callback passed to map, and returns a new functor, which contains the structure as the first functor, but with the transformed values.
src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76