I'm trying to get familiar with functional programming in JavaScript. I've just read that pointer functor is:
An object with an
of
function that puts any single value into it.ES2015 adds
Array.of
making arrays a pointed functor.
And my question is what does exactly mean "single value"?
I want to make a Functor/Container (like in https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch8.html) that holds grid of given dimension (width, height) as 1-dimensional array and allows me to do transformations on it. As a plain object I would store it as { width: 2, height: 2, list: [1, 2, 3, 4] }
but I want to put it in a functor and I'm not sure how to do properly.
I know that it's perfectly fine to use pointed functor like this to store single value:
Container.of(47)
But is it ok to use object as value assuming object is a "single value":
Grid.of({ width: 2, height: 2, list: [1, 2, 3, 4] })
Or even like this:
Grid.of(2, 2, [1, 2, 3, 4])
Yes.
of
is supposed to take any value and put it inside the container. An object certainly is such a single value.No.
of
is supposed to take a single parameter. If you want to put multiple values inside a functor, put them inside an other structure before and put that structure inside the functor, or construct the functor by something else than its point function (of
).No, if you expect that to return the input then it won't work.
of
should take the input as-is and wrap the structure around it. In case of your grid, it would most certainly look like this:So the above call would create a
Grid
of objects, not a grid of four numbers. Notice thatof
is not the only way to construct an instance of a functor, it's only the way to construct an instance from a single element.Notice that
of
is most important as part of an Applicative, not so much interesting for ordinary Functors. Btw, if you're interested in functional programming concepts, you should also be able to make yourGrid
a Monoid, a Traversable and a Monad - see https://github.com/fantasyland/fantasy-land.The explanation in https://github.com/hemanth/functional-programming-jargon is unfortunately not very accurate.
A pointed functor is really a functor
F
together with a functionof
defined for every typea
, and sending a valuex
of typea
into a valueof(x)
of typeF a
. In Hindley-Milner signature it looks like this:For instance, the Array functor is pointed with
of = x => [x]
, defined for every valuex
of any typea
.Further, the function
of
(or more precisely, the collection of functionsof
as you have one for each typea
) must be a natural transformation from the identity functor intoF
. Which means thatof
applied to a function's value equalsof
applied to the function's argument and then mapped over the function:For instance, in the Array example you have
so
x => [x]
is indeed a natural transformation.But you can also redefine
of
aswhich makes
Array
into another pointed functor, even if themap
method remains the same. (Note that in each case, you only get very special arrays as values ofof(x)
.)However, you cannot define your
of
as e.g.Now
is perfectly ok and returns your object passed wrapped into
Grid
. Then you can map yourgrid
with any regular functionf
from plain objects into plain objects and the result will be the same as applyingf
and wrapping intoGrid
, because of the natural transformation law. Note that this way you can also callGrid.of
with any other value likeGrid.of({width: 2})
of evenGrid.of(2)
. Alternatively, you can restrict the types for whichGrid.of
is defined, then the value must only be of a type that you allow.This one is a bit tricky:
This applies
Grid.of
to several arguments. SinceGrid.of
is by definition a function of only one argument, the result will beGrid.of(2)
, which may not be what you want. If you really want to feed all values, you probably want to writeAlternatively, you can extend
Grid.of
to multiple arguments by pre-wrapping them into an array internally and then applyingGrid.of
. It really depends on what you are after.For a real world usage example, see e.g. here where a "boring" Task is defined via
Task.of
from a plain value. On the other hand here is a more interesting Task wrapping a function that you wouldn't get withTask.of
. What is important though, is that both Tasks can be used with the same uniform interface as shown on both examples.Also note that no applicative functors are used in these examples, so there are still uses of pointed functors without being applicative.
ADDED.
See also https://github.com/MostlyAdequate/mostly-adequate-guide-it/blob/master/ch9.md#pointy-functor-factory for a nice introduction and real world uses of the Pointed Functor.