gpt4 book ai didi

javascript - 如何将此JavaScript代码转换为功能方式(最好使用Ramdajs)?

转载 作者:行者123 更新时间:2023-11-30 09:36:58 27 4
gpt4 key购买 nike

我正在尝试创建具有接口的设置模块,该接口可以提供用于更改和读取设置对象的功能。

现在代码看起来像这样:

let _settings = {
user: {
id: userId || '',
authorized: false,
}
}

function getSettings() {
return _settings
}

function addParamToSettings(param) {
_settings = {
..._settings,
...param,
}
}

function addParamToUser(param) {
addParamToSettings({
user: {
...getSettings().user,
...param,
},
})
}

let saveUserId = function saveUserId(id) {
addParamToUser({ id, authorized: true })
}


我想以更实用的方式(例如默认样式,使用镜头,不可变等)重写此代码,更好地使用Ramda js

首先,我不了解如何以功能方式工作,然后您需要某种可以读取和写入其中的信息存储(例如本例中的对象_settings)。

我试图重写addParamToApp函数,最好的镜头是:

const userLense = R.lensProp('user')

const _addParamToUser = R.compose(R.set(userLense, R.__, _settings), R.merge(_settings.user))


但是我仍然需要编写函数来处理_settings更改

const addParamToUser = function addParamToUser(param){
_settings = _addParamToUser(param)
}


在我看来,这似乎不对。还有更多的代码,然后第一次实现。

我该如何以更实用的方式编写它?如何使用读写功能处理信息存储?

最佳答案

国家莫纳德

您可能有兴趣探索State monad。我将首先浏览程序的两个部分,然后在底部包括一个完整的可运行示例。

首先,我们将介绍State monad。网上有不计其数的monad介绍,这些内容超出了本文的讨论范围,因此,我将只介绍足够的内容,以帮助您入门和运行。 State monad的其他实现将有更多便利;如果您有兴趣了解更多信息,请务必了解这些内容。

// State monad
const State = runState => ({
runState,
bind: f => State(s => {
let { value, state } = runState(s)
return f(value).runState(state)
}),
evalState: s =>
runState(s).value,
execState: s =>
runState(s).state
})

State.pure = y =>
State(x => ({ value: y, state: x }))

State.get = () =>
State(x => ({ value: x, state: x }))

State.put = x =>
State($ => ({ value: null, state: x }))




规则优先

与所有单子一样,要成为单子,它必须满足以下三个单子法。


左身份: pure(a).bind(f) == f(a)
正确的身份: m.bind(pure) == m
关联性: m.bind(f).bind(g) == m.bind(x => f(x).bind(g))


其中 pure是我们将值放入Monad实例的方式, m,而 bind是我们与实例包含的值进行交互的方式–您有时会在此处将 pure称为 return其他有关monad的描述,但我将避免在此答案中使用 return,因为它是JavaScript中的关键字,与monad无关。

不必太担心了解实现细节。当您刚开始时,最好对State monad的工作方式有一个直观认识。稍后,我们将看到使用state monad的示例程序



您的第一个有状态功能

假设您的初始程序状态为 {},我们将看一个修改状态以添加 user和相应的 userId的函数。这可能是在数据库中查找用户的结果,并且我们将 authorized属性设置为 false,直到我们稍后验证用户密码正确无误为止

const setUser = userId =>
State.get().bind(settings =>
State.put(Object.assign({}, settings, {
user: { userId, authorized: false }
})))


该功能的第1步是使用 State.get()来获取状态-似乎它什么也不做,但是在调用此功能的上下文中,它产生了明显的不同。稍后,您将看到我们在哪里使用初始状态值执行计算,而 State.get似乎无足轻重。再次,就目前而言,仅凭直觉就可以假设我们以某种方式获得了状态。

步骤2是 bind(settings => ...位。回想一下 bind是我们如何与状态值进行交互,在这种情况下,它就是我们的 settings对象。因此,我们在此真正要做的只是更新 settings以包含设置为 user{ userId, authorized: false }属性。调用 setUser之后,我们可以认为我们的状态具有新的形状

// old state
let settings = {...}

// new state
settings = { ...settings, { user: { userId, authorized: false } } }




第二个有状态功能

好的,您的用户想立即登录。让我们接受一个 challenge并将其与某些密码进行比较-如果用户提供了正确的密码,我们将更新状态以显示 authorized: true,否则我们将其设置为 false

const PASSWORD = 'password1'

const attemptLogin = challenge =>
State.get().bind(settings => {
let { userId, authorized } = settings.user
if (challenge === PASSWORD)
return State.put(Object.assign({}, settings, {
user: { userId, authorized: true }
}))
else
return State.pure(settings)
})


步骤1是重新获取状态,就像上一次一样

步骤2是 bind,可以访问状态值并执行某些操作。回想一下我们在上一状态修改(上一节末尾的 new state)中保留的地方:要读取用户的当前状态,我们要读取 settings.user

步骤3是决定下一个状态是什么:如果用户提供了正确的 challenge(即等于 PASSWORD),则我们将返回一个新的状态,将 authorized设置为 true –否则,如果挑战与密码不匹配,请使用 State.pure(settings)返回未修改的状态



您的第一个有状态程序

因此,现在我们有了两个函数( setUserattemptLogin),它们分别读取状态和返回状态。现在编写我们的程序很容易

const initialState = {}

const main = (userId, challenge) =>
setUser(userId)
.bind(() => attemptLogin(challenge))
.execState(initialState)


而已。运行下面的完整代码示例,以查看两种登录方案的输出:一种使用有效密码,一种使用无效密码



完整的代码示例



// State monad
const State = runState => ({
runState,
bind: f => State(s => {
let { value, state } = runState(s)
return f(value).runState(state)
}),
evalState: s =>
runState(s).value,
execState: s =>
runState(s).state
})

State.pure = y =>
State(x => ({ value: y, state: x }))

State.get = () =>
State(x => ({ value: x, state: x }))

State.put = x =>
State($ => ({ value: null, state: x }))

// your program
const PASSWORD = 'password1'
const initialState = {}

const setUser = userId =>
State.get().bind(settings =>
State.put(Object.assign({}, settings, {
user: { userId, authorized: false }
})))

const attemptLogin = challenge =>
State.get().bind(settings => {
let { userId, authorized } = settings.user
if (challenge === PASSWORD)
return State.put(Object.assign({}, settings, {
user: { userId, authorized: true }
}))
else
return State.pure(settings)
})

const main = (userId, challenge) =>
setUser(userId)
.bind(() => attemptLogin(challenge))
.execState(initialState)

// good login
console.log(main(5, 'password1'))
// { user: { userId: 5, authorized: true } }

// bad login
console.log(main(5, '1234'))
// { user: { userId: 5, authorized: false } }







然后去哪儿?

这只是在状态monad上刮擦了表面。我花了很长时间了解它的工作原理,但我仍然没有掌握它。

如果您对工作原理scratch之以鼻,我强烈建议您采用旧的笔/纸评估策略-跟踪程序并谨慎选择替换方案。当您看到它们融合在一起时,真是太神奇了。

并确保通过各种来源对State monad进行更多阅读


wikipedia.org: Monad (functional Programming) - State Monad
haskell.org: State Monad
learnyouahaskell.com: For a few Monads More - Tasteful Stateful Computations
还有其他你能找到的




“最好使用Ramda吗?”

我认为您的问题的一部分是,您缺乏以功能性方式推理程序的一些基础知识。到达Ramda之类的图书馆不太可能帮助您发展技能或提供更好的直觉。以我自己的经验,我会通过坚持基础知识并从那里建立基础来学习最好。

作为一门学科,您可以练习使用任何Ramda函数,然后再使用它。只有这样,您才能真正知道/欣赏Ramda带来的收益。

关于javascript - 如何将此JavaScript代码转换为功能方式(最好使用Ramdajs)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43016716/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com