Javascript 重构对FP样式有副作用的简单算法-更新任务状态

Javascript 重构对FP样式有副作用的简单算法-更新任务状态,javascript,functional-programming,ramda.js,Javascript,Functional Programming,Ramda.js,我们有一个基本算法,它是以命令式风格编写的: const db = new DB() const markAsCompleted = ( taskId ) => { // Mark the task as completed db.markTaskAsCompleted( taskId ) // Update parent status const parentId = db.getTaskParentId( taskId ) if ( par

我们有一个基本算法,它是以命令式风格编写的:

const db = new DB()

const markAsCompleted = ( taskId ) => {
    // Mark the task as completed
    db.markTaskAsCompleted( taskId )

    // Update parent status 
    const parentId = db.getTaskParentId( taskId )
    if ( parentId ) {

        // Get parent's incomplete children
        const isIncomplete = ( task ) => task.status != 'completed'
        const parentChildren = db.getTaskChildren( parentId )
        const incompleteTasks = parentChildren.filter( isIncomplete )

        // If all children have completed, mark the parent as completed
        if ( incompleteTasks.length === 0 ) {
            db.markTaskAsCompleted( taskId ) // Simple case - no recursion
            // markAsCompleted( parentId ) // Complex case - with recursion
        }
    }
}
db操作都涉及副作用(显然)

如何将其转换为函数式编程风格?也就是说,一种基于函数组合的无点风格,同时使用
IO
monad并让客户端“触发”副作用。在这方面:

优选地,该溶液将使用或

还应注意以下两种情况:

  • 简单,无递归(未注释)
  • 带递归的复数(注释)

通常db查找是异步的,因此使用异步adt代替io adt,但当您要求io时

在数据库周围创建一个包装器,该包装器可以返回计算的ios,然后链接:

// replace return values of these functions with your actual db lookups
const db = {
    getTaskChildren: (taskId) => IO.of([]),
    getTaskParentId: (taskId) => IO.of('123'),
    markTaskAsCompleted: (taskId) => IO.of(1),
};

const checkParent = (parentId) =>
    db.getTaskChildren(parentId)
        .map((children) =>
            children.filter(({ status }) => status !== 'completed'))
        .chain((incomplete) =>
            incomplete.length === 0
                ? db.markTaskAsCompleted(parentId)
                : IO.of(null)); // do nothing

const markAsCompleted = (taskId) =>
    db.markTaskAsCompleted(taskId)
        .chain(() => db.getTaskParentId(taskId))
        .chain(checkParent);

markAsCompleted('abc').run();

如果io交换为异步,则流不应更改

通常db查找是异步的,因此使用异步adt而不是io adt,但是当您要求io时

在数据库周围创建一个包装器,该包装器可以返回计算的ios,然后链接:

// replace return values of these functions with your actual db lookups
const db = {
    getTaskChildren: (taskId) => IO.of([]),
    getTaskParentId: (taskId) => IO.of('123'),
    markTaskAsCompleted: (taskId) => IO.of(1),
};

const checkParent = (parentId) =>
    db.getTaskChildren(parentId)
        .map((children) =>
            children.filter(({ status }) => status !== 'completed'))
        .chain((incomplete) =>
            incomplete.length === 0
                ? db.markTaskAsCompleted(parentId)
                : IO.of(null)); // do nothing

const markAsCompleted = (taskId) =>
    db.markTaskAsCompleted(taskId)
        .chain(() => db.getTaskParentId(taskId))
        .chain(checkParent);

markAsCompleted('abc').run();
如果io交换为异步,则流不应更改

确保使用
if(children.every(isCompleted))
不要使用可怕的筛选器+长度解决方案确保使用
if(children.every(isCompleted))
不要使用可怕的筛选器+长度解决方案