2011-08-27 42 views
4

我在面向文檔的前面添加了一個安全層,我需要一種合理的抽象方式來定義應用程序定義哪些新文檔以及現有文檔的哪些更新對特定用戶是合法的。好的,是* this * monadic?

特定問題是 - 文檔被定義(至少在傳輸中)爲JSON對象,所以規則可能是分層的,因此規則引擎必須遞歸地工作。例如,Employee對象可能有一個名爲Compensation的子對象,並且該子對象有一個名爲PayPeriod的字段,該字段必須是「每週」,「雙週」或「每月」之一。 - 它運行Node.js,一些規則需要從輸入中讀取(例如,從數據庫中讀取更多用戶數據),因此它必須以連續方式運行。

所以我想到的是:每個規則是一個函數,它接受當前值,建議的新值以及與要使用的值一起調用的回調函數。該值可以是規則計算的兩個輸入值或第三個值中的一個。以下是一條規則:

var nonEmpty = function(proposedValue, existingValue, callback) { 
    callback((proposedValue.length > 0) ? proposedValue : existingValue); 
}; 

此規則只允許您設置或用非零長度值替換此字段。當然,這僅是有道理的字符串值(暫時忽略列表,所以我們需要一個規則來執行字符串岬):

var isString = function(proposedValue, existingValue, callback) { 
    callback((typeof(proposedValue) === 'string') ? proposedValue : existingValue); 
}; 

事實上,這似乎是問題一個不同凡響的,所以我寫了一個規則生成:

var ofType = function(typeName) { 
    return function(proposedValue, existingValue, callback) { 
    callback((typeof(proposedValue) === typeName) ? proposedValue : existingValue); 
    }; 
}; 

var isString = ofType('string') 

,但我需要一種方法來串在一起的規則:

var and = function(f1, f2) { 
    return function(proposedValue, existingValue, callback) { 
    f1(proposedValue, existingValue, 
     function(newProposedValue) { 
      f2(newProposedValue, existingValue, callback); 
     }); 
    }; 
}; 

var nonEmptyString = and(isString, nonEmpty); 

所以對於一個管理員聯繫,以更新員工記錄的規則可能是:

limitedObject({ 
    lastName : nonEmptyString, 
    firstName : nonEmptyString, 
    compensation : limitedObject({ 
     payPeriod : oneOf('weekly', 'biweekly', 'monthly'), 
     pay : numeric 
    } 
}) 

limitedObject(如ofType)是規則的生成函數,並且它僅允許在其參數指定的字段並施加給定的規則對這些字段的值。

所以我寫了所有這些,它就像一個魅力。我所有的錯誤都是單元測試中的錯誤!好吧,幾乎所有的人。無論如何,如果你已經讀了這麼多,這裏是我的問題:

我一直熱情地學習單子,我的閱讀激發了我用這種方式解決問題。但是,這是真正的一元嗎?

(可能的答案:「是的」,「不,但是沒關係,因爲單子不是真正解決這個問題的正確方法」,「不,這是需要改變的地方」。

回答

4

不,這似乎不是一元。您定義的內容似乎是規則組合器的小型DSL,其中您有簡單規則(如ofType(typeName))以及將規則組合成更大規則(如and(rule1, rule2))的方法。

爲了擁有monad,您需要一些可以放置任何值的上下文概念。您還需要以下操作:

  1. 爲把任何值到一些默認的上下文中的功能wrap(x)
  2. 函數map(f, m)用於應用函數f以在不改變上下文的情況下轉換m中的值。
  3. 功能flatten(mm)用於將兩層上下文平鋪爲一個上下文。

這些操作必須滿足一定的「明顯」的規律:

  1. 添加在外面背景層和崩潰給你回你開始用什麼。

    flatten(wrap(m)) == m 
    
  2. 在內部添加一層上下文並摺疊,可以讓您重新開始。

    flatten(map(wrap, m)) == m 
    
  3. 如果你有上下文的三層的價值,它不會不管你是否崩潰的兩個內或兩個外層第一。

    flatten(flatten(mmm)) == flatten(map(flatten, mmm)) 
    

另外,也可以在如上述wrap術語和另一個操作bind以限定單子,然而,這等效於上面的,因爲你可以在mapflatten來定義bind,和反之亦然。

function bind(f, m) { return flatten(map(f, m)); } 

# or 

function map(f, m) { return bind(function(x) { return wrap(f(x)); }, m); } 
function flatten(mm) { return bind(function(x) { return x; }, mm); } 

目前尚不清楚上下文的概念會是什麼在這裏,你將如何把任何值的規則。因此,如何扁平化兩層規則的問題更加不合理。

我不認爲monad是一個合適的抽象。

但是,很容易發現您的and確實形成了一個monoid,其中總是成功的規則(如下所示)作爲標識元素。

function anything(proposedValue, existingValue, callback) { 
    callback(proposedValue); 
} 
+1

* combinators * - 這是我正在尋找的模式的名稱。它感到非常熟悉。 – Malvolio

+0

這是我見過的關於Javascript開發者的monad法則中最容易實現的完美! – rand

相關問題