[Functional Programming] Working with two functors(Applicative Functors)-- Part1 --.ap

What is applicative functor:

the ability to apply functors to each other.

For example we have tow functors: Container(2), Container(3)

// We can't do this because the numbers are bottled up.
add(Container.of(2), Container.of(3));  // NaN

We cannot just add two functors!

 

Instead we should do:

const map = (fn, m) => m.map(fn);
const containerOfAdd2 = map(add(3), Container.of(2)); // Container(5)

or

Container.of(2).chain(two => Container.of(3).map(add(two)));

 

Previous solution should work. but there are better way to do it:

1. ap

Container.prototype.ap = function (otherContainer) {
  return otherContainer.map(this.$value);
};

As you can see, 'ap' takes a fuctor then applya map to it.

We can see ap:

Container.of(2).map(add).ap(Container.of(3)); // Container(5)

Or, we add lift 'add(2)' into Container, then apply Container(3):

Container.of(add(2)).ap(Container.of(3)); // Container(5)

Because 'add' is partially  applied in add(2), when doing '.ap(Container.of(3))', we give the rest input '3' to it.

Now, we can define applicative functor in programming language:

An applicative functor is a pointed functor with an ap method

Note the dependence on pointed

 

Laws behind:

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

Main idea is: lift 'f' (function) into Functor, then 'ap' (apply) another Functor with the value (x).

Some example:

Maybe.of(add).ap(Maybe.of(2)).ap(Maybe.of(3)) // Just(5)
Task.of(add).ap(Task.of(2)).ap(Task.of(3)) // Task(5)

Equals:

Maybe.of(add(2)).ap(Maybe.of(3)) // Just(5)
Task.of(add(2)).ap(Task.of(3)) // Task(5)

 

More examples:

// Http.get :: String -> Task Error HTML

const renderPage = curry((destinations, events) => { /* render page */ });

Task.of(renderPage).ap(Http.get('/destinations')).ap(Http.get('/events'));
// Task("<div>some page with dest and events</div>")
// $ :: String -> IO DOM
const $ = selector => new IO(() => document.querySelector(selector));

// getVal :: String -> IO String
const getVal = compose(map(prop('value')), $);

// signIn :: String -> String -> Bool -> User
const signIn = curry((username, password, rememberMe) => { /* signing in */ });

IO.of(signIn).ap(getVal('#email')).ap(getVal('#password')).ap(IO.of(false));
// IO({ id: 3, email: 'gg@allin.com' })

 

----

const R = require('ramda');

class Container {
    static of(x) {
        return new Container(x);
    }

    constructor(x) {
        this.$value = x;
    }

    map (fn) {
        return Container.of(fn(this.$value));
    }

    ap (functor) {
        return functor.map(this.$value);
    }

    join() {
        return this.$value; 
    }

    chain(fn) {
        return this.map(fn).join();
    }

    inspect() {
        return `Container(${this.$value})`;
    }
}

class Maybe {
    get isNothing() {
      return this.$value === null || this.$value === undefined;
    }
  
    get isJust() {
      return !this.isNothing;
    }
  
    constructor(x) {
      this.$value = x;
    }
  
    inspect() {
      return this.isNothing ? 'Nothing' : `Just(${this.$value})`;
    }
  
    // ----- Pointed Maybe
    static of(x) {
      return new Maybe(x);
    }
  
    // ----- Functor Maybe
    map(fn) {
      return this.isNothing ? this : Maybe.of(fn(this.$value));
    }
  
    // ----- Applicative Maybe
    ap(f) {
      return this.isNothing ? this : f.map(this.$value);
    }
  
    // ----- Monad Maybe
    chain(fn) {
      return this.map(fn).join();
    }
  
    join() {
      return this.isNothing ? this : this.$value;
    }
  
    // ----- Traversable Maybe
    sequence(of) {
      this.traverse(of, identity);
    }
  
    traverse(of, fn) {
      return this.isNothing ? of(this) : fn(this.$value).map(Maybe.of);
    }
  }

  const add = a => b => a + b;
  const map = (fn, m) => m.map(fn);
  const notWorking = add(Container.of(2), Container.of(3));
  const containerOfAdd2 = map(add(3), Container.of(2));
  console.log(containerOfAdd2); // Contianer(5)

  const works = Container.of(2).chain(v => Container.of(3).map(add(v)));
  console.log(works); // Contianer(5)

  const ap = Container.of(2).map(add).ap(Container.of(3));
  console.log(ap)

  const ap2 = Container.of(add(2)).ap(Container.of(3));
  console.log(Maybe.of(add).ap(Maybe.of(2)).ap(Maybe.of(3)))
  console.log(Maybe.of(add(2)).ap(Maybe.of(3)))

  

posted @ 2019-02-25 19:12  Zhentiw  阅读(213)  评论(0编辑  收藏  举报