Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use pointed functor properly

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])
like image 577
jesper Avatar asked Aug 27 '16 10:08

jesper


People also ask

What is a pointed functor?

A pointed functor is really a functor F together with a function of defined for every type a , and sending a value x of type a into a value of(x) of type F a .

What is the use of functor?

A C++ functor (function object) is a class or struct object that can be called like a function. It overloads the function-call operator () and allows us to use an object like a function.

How do you call a functor?

Functors are called using the same old function call syntax. To create a functor, we create a object that overloads the operator(). The line, MyFunctor(10); Is same as MyFunctor. operator()(10);

What is the difference between a function and a functor?

A function assigns to every element of a set X an element of a set Y. A functor assigns to every object of a category C an object of a category D and also assigns to every morphism in C a morphism in D in a way compatible with sources, targets, and composition.


1 Answers

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 function of defined for every type a, and sending a value x of type a into a value of(x) of type F a. In Hindley-Milner signature it looks like this:

of :: a -> F a

For instance, the Array functor is pointed with of = x => [x], defined for every value x of any type a.

Further, the function of (or more precisely, the collection of functions of as you have one for each type a) must be a natural transformation from the identity functor into F. Which means that of applied to a function's value equals of applied to the function's argument and then mapped over the function:

of(f(x)) === of(x).map(f)

For instance, in the Array example you have

[f(x)] === [x].map(f),

so x => [x] is indeed a natural transformation.

But you can also redefine of as

of = x => [x, x]
[f(x), f(x)] === [x, x].map(f)

which makes Array into another pointed functor, even if the map method remains the same. (Note that in each case, you only get very special arrays as values of of(x).)

However, you cannot define your of as e.g.

of = x => [x, 0]
[f(x), 0] !== [x, 0].map(f)

Now

var grid = Grid.of({ width: 2, height: 2, list: [1, 2, 3, 4] })

is perfectly ok and returns your object passed wrapped into Grid. Then you can map your grid with any regular function f from plain objects into plain objects and the result will be the same as applying f and wrapping into Grid, because of the natural transformation law. Note that this way you can also call Grid.of with any other value like Grid.of({width: 2}) of even Grid.of(2). Alternatively, you can restrict the types for which Grid.of is defined, then the value must only be of a type that you allow.


This one is a bit tricky:

Grid.of(2, 2, [1, 2, 3, 4])

This applies Grid.of to several arguments. Since Grid.of is by definition a function of only one argument, the result will be Grid.of(2), which may not be what you want. If you really want to feed all values, you probably want to write

Grid.of([2, 2, [1, 2, 3, 4]])

Alternatively, you can extend Grid.of to multiple arguments by pre-wrapping them into an array internally and then applying Grid.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 with Task.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.

like image 136
Dmitri Zaitsev Avatar answered Oct 05 '22 23:10

Dmitri Zaitsev