Skip to content

函子 #18

@clumsyme

Description

@clumsyme

函子(Functor)

haskell 内的 Functor 定义:

class Functor f where 
   fmap :: (a -> b) -> f a -> f b 

简单来说,Functor 就是一个定义了 fmap 方法的对象,它可以被作为 map 函数的参数。

map 接收一个 a -> b 类型的函数和一个函子 f a,返回一个新的函子 f b 类型的函数。

function plusOne(x) {
    return x + 1
}

plusOne([3]) // ! wrong
R.map(plusOne, [3]) // [4]

// ---> [3] -> [plsuOne(3)]

R.map(plusOne, MyObject(1)) // MyObject(plusOne(1))
//=> MyObject(2)

R.map(function(x) { return 'I am ' + x}, MyObject("Yo")) // MyObject("I am Yo")
R.map(function(x) { return x.id}, MyObject({id: 2718})) // MyObject(2718)

// map 方法就像是打开 MyObject,对 MyObject 内的值进行操作,再关闭 MyObject

class MyObject {
    constructor(value) {
        this.__value = value
    }

    map(func) {
        return new MyObject(func(this.__value))
    }
}

// 我们通过对 MyObject 定义 map, MyObject 可以看做一个 Functor

// some use of Functor
// type safe
class Maybe {
    constructor(value) {
        this.__value = value
    }

    map(func) {
        return this.__value ? new Maybe(func(this.__value)) : new Maybe(null)
    }
}

R.map(plusOne, new Maybe(1)) // Maybe(2)
R.map(plusOne, new Maybe(null) // Maybe(null)

// 对于 Functor,我们需要调用 map 方法来修改它的值,而不能直接修改它的值。
//! 动态的类型安全

// default value
class Either {
    // left as default
    constructor(left, right) {
        this.__left = left
        this.__right = right
    }

    map(func) {
        return this.right 
            ? new Either(this.__left, func(this.__right)) 
            : new Either(func(this.__left), this.__right)
    }
}

R.map(plusOne, new Either(1, 2)) // Either(1, 3)
R.map(plusOne, new Either(1, null)) // Either(2, null)

// auto handle Promise
Promise.prototype.map = function(func) {
    let thisPromise = this
    return this.then(function(response) {
        return func(response)
    })
}

function handleResponse(response) {
    return response.json().then(json => {
        // doSthWithJson
        return 'done'
    })
}

let mapped = R.map(handleResponse, fetch('/someAPI'))

await mapped // 'done'


// DEMO
// we have a side effect
let setProp = function (prop, value, obj) {
  obj[prop] = value
  return obj
}.autoCurry()

let title = document.getElementById('title')
// getGreeting :: User -> String
let getGreeting = compose(R.concat("Welcome "), R.prop("name"))
// updateGreetingHtml :: User -> undefined
let updateGreetingHtml = compose(setProp('textContent', R.__, title), getGreeting)

updateGreetingHtml(currentUser)

// what if we dont have a currentuser
map(updateGreetingHtml, Either({name: 'Guest'}, currentUser))

// 使用 Functor,你不能直接操作 Functor 的值,在程序其他地方必须使用 map(包括 filter) 来进行操作,
// 你不必关心其内部结构,只用知道他可以被 map

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions