zoukankan      html  css  js  c++  java
  • [Compose] 21. Apply Natural Transformations in everyday work

    We see three varied examples of where natural transformations come in handy.

    Let's mark the law here for Natural Transformations:

    nt(F).map(f) === nf(F.map(f))

    Then let's see why we might need to apply Natural Transformations:

    1. Natural Transformations can improve the code proformance

    For example, we have an array of numbers, we want to get larger number, apply a doulbe function, then only get first element out of the array.

    const first = xs => fromNullable(xs[0]);
    const largeNumbers = xs => xs.filter(x => x > 100);
    const doulbe = x => x * 2;
    
    // We first run though the whole array to doulbe the number
    // Then apply first to either transform -> Proformance cost
    // Since nt(F).map(f) === nt(F.map(f))
    const transform1 = xs => first(largeNumbers(xs).map(doulbe));
    const app = xs => transform1(xs);
    console.log(app([2,400,5,100])); // Either { value: 800 }

    Imporved one should be get only first of larger number, then apply transform:

    const first = xs => fromNullable(xs[0]);
    const largeNumbers = xs => xs.filter(x => x > 100);
    const doulbe = x => x * 2;
    
    // Now we get first of array and transform to either, then apply our doulbe function
    const transform2 = xs => first(largeNumbers(xs)).map(doulbe);
    const app = xs => transform2(xs);
    console.log(app([2,400,5,100])); // Either { value: 800 }

    2. Solve different nested Functors:

    const fake = id =>
        ({id, name: 'user1', best_friend_id: id + 1});
    // Db :: find :: id -> Task(Either)
    const Db = ({
        find: id => new Task((rej, res) => {
            res(id > 2 ? Right(fake(id)): Left('not found'))
        })
    });

    For example, the code above, we have a Db.find function, which return Task(Either(User)). It would be much easier for us to work with the same Functor type, instead of multi functors. we can think about change

    // Either -> Task
    const eitherToTask = e =>
        e.fold(Task.rejected, Task.of);

    With this tool, let's see how it solve us problem:

    Db.find(4) // Task(Right(User))

    Now we chain 'eitherToTask':

    Db.find(4) // Task(Right(User))
        .chain(eitherToTask) // // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)

    So after run though 'eitherToTask' we change 'Right to Task' we got 'Task(Task(User)), then the outmost 'chain' will remove 'Task(User)' 

    Now if we want to find User's best friend id:

    Db.find(4) // Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User))

    We know that 'Db.find(user.best_friend_id)' returns 'Task(Right(User))', inner most function call return 'Task(Task(Right(User)))', after outter most 'chain', we got 'Task(Right(User))'.

    Now we can apply the trick again:

    Db.find(4) // Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)

    We only got 'Task(User)' in the end, ready to be forked!

    Db.find(4) // Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .fork(console.error, console.log); // { id: 5, name: 'user1', best_friend_id: 6 }

    -- 

    const Either = require('data.either');
    const Task = require('data.task');
    const {List, Map} = require('immutable-ext');
    const {Right, Left, fromNullable} = Either;
    
    const log = console.log;
    
    // F a -> G a
    // nt(F).map(f) === nt(F.map(f))
    
    //////Exp1/////
    const res = List(['Hello', 'wolrd']).chain(x => x.split(''));
    console.log(res.toJS()); // [ 'H', 'e', 'l', 'l', 'o', 'w', 'o', 'l', 'r', 'd' ]
    
    //////Exp2/////
    const first = xs => fromNullable(xs[0]);
    const largeNumbers = xs => xs.filter(x => x > 100);
    const doulbe = x => x * 2;
    
    // We first run though the whole array to doulbe the number
    // Then apply first to either transform -> Proformance cost
    // Since nt(F).map(f) === nt(F.map(f))
    const transform1 = xs => first(largeNumbers(xs).map(doulbe));
    // Now we get first of array and transform to either, then apply our doulbe function
    const transform2 = xs => first(largeNumbers(xs)).map(doulbe);
    const app = xs => transform2(xs);
    console.log(app([2,400,5,100])); // Either { value: 800 }
    
    //////Exp3/////const fake = id =>
        ({id, name: 'user1', best_friend_id: id + 1});
    // Db :: find :: id -> Task(Either)
    const Db = ({
        find: id => new Task((rej, res) => {
            res(id > 2 ? Right(fake(id)): Left('not found'))
        })
    });
    
    const eitherToTask = e =>
        e.fold(Task.rejected, Task.of);
    
    Db.find(4) // Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User))
        .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
        .fork(console.error, console.log); // { id: 5, name: 'user1', best_friend_id: 6 }
  • 相关阅读:
    美多商城项目(一)
    Linux安装Qt
    mysql之初体验
    Linux系统编程目录
    Linux 多线程
    进程间通信
    Linux进程
    Linux文件IO(简易)
    Linux常用基本操作
    重绘
  • 原文地址:https://www.cnblogs.com/Answer1215/p/6215862.html
Copyright © 2011-2022 走看看