# 函子 #18

Open
opened this issue Oct 18, 2019 · 0 comments

## 函子(Functor)

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

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

```function plusOne(x) {
return x + 1
}

plusOne() // ! wrong```
```R.map(plusOne, ) // 

// --->  -> [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```