Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函子 #18

Open
clumsyme opened this issue Oct 18, 2019 · 0 comments

Comments

@clumsyme
Copy link
Owner

@clumsyme clumsyme commented Oct 18, 2019

函子(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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.