[英]How to translate this javascript code into functional way (better using Ramdajs)?
我正在嘗試創建具有接口的 設置模塊 ,該接口可以提供用於更改和讀取設置對象的功能。
現在代碼看起來像這樣:
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
是我們把一個值的一個實例的方式我們的單子, m
,並且bind
是我們與實例的包含價值互動的方式-有時你會在這里pure
叫return
在單子的其他描述,但我會避免在此答案中使用return
,因為它是JavaScript中的關鍵字,與monad無關。
不必太擔心了解實現細節。 當你開始,這是更好地制定有關國家單子是如何工作的直覺。 稍后,我們將看到使用state monad的示例程序
您的第一個有狀態功能
假設您的初始程序狀態為{}
,我們將看一個修改狀態以添加user
和對應的userId
的函數。 這可能是由於在數據庫中查找用戶而導致的,我們將authorized
屬性設置為false
直到稍后我們驗證用戶密碼是否正確
const setUser = userId =>
State.get().bind(settings =>
State.put(Object.assign({}, settings, {
user: { userId, authorized: false }
})))
該函數的第一步是使用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是重新獲取狀態,就像上一次一樣
bind
步驟2,以訪問狀態值並執行某些操作。 回想一下我們在上一個狀態修改(上一節末尾的new state
中保留的地方:要讀取用戶的當前狀態,我們要讀取settings.user
。
步驟3決定下一個狀態:如果用戶提供了正確的challenge
(即等於PASSWORD
),我們將返回authorized
狀態設置為true
的新狀態–否則,如果質詢與密碼,使用State.pure(settings)
返回未修改的狀態
您的第一個有狀態程序
所以現在我們有兩個函數( setUser
和attemptLogin
),它們分別讀取狀態和返回狀態。 現在編寫我們的程序很容易
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進行更多閱讀
“最好使用Ramda嗎?”
我認為您的問題的一部分是,您缺乏以功能性方式推理程序的一些基礎知識。 到達Ramda之類的圖書館不太可能幫助您發展技能或提供更好的直覺。 以我自己的經驗,我會通過堅持基礎知識並從那里建立基礎來學習最好。
作為一門學科,您可以練習使用任何Ramda函數,然后再使用它。 只有這樣,您才能真正知道/欣賞Ramda帶來的收益。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.