【问题标题】:Functional programming and local function state assignments函数式编程和局部函数状态分配
【发布时间】:2017-04-18 01:42:31
【问题描述】:

我最近将代码转换为我认为至少部分是功能性 javascript。 这是一个 Web 表单验证过程。在流程的每一步,函数都会执行自己的验证层并在将数据传递给下一个函数之前对其进行修改。

1) 我的第一个问题是……下面的代码是否可以被认为至少是“部分”功能的?

您会注意到,在 requiredFields 中,我创建了两个 assignment... 状态。它们是函数的局部变量,函数没有外部副作用....

2) 在函数式编程范式的上下文中,这种本地分配是不好的做法吗?这是否被视为“局部副作用”?

这是模型:

function linkmodel() {
    return {
        title: { 
            content: undefined,
            validation: {
                type: "string",
                required: true,
                minLength: 1,
                maxLength: 3,
                validationErrorMessage: "Your title must be a valid string between 1 and 35 characters"
            }
        },

        email: {
            content: undefined,
            validation: {
                type: "email",
                required: true,
                minLength: 1,
                maxLength: 60,
                validationErrorMessage: "Your email must be between 1 and 50 characters"
            }
        },


        link: {
            content: undefined,
            validation: {
                type: "url",
                required: true,
                minLength: 1,
                maxLength: 500,
                validationErrorMessage: "Your link name must be a valid email between 1 and 50 characters"
            }
        },

        description: {
            content: undefined
        }
    }
}


export default linkmodel

这是验证系统:

app.post( "/", function( req, res ) {
  let form = new forms.IncomingForm()

  form.parse( req, function( err, fields, files ) {

    // Lodash launching the function flow
    let errorMessageBag = _.flow( objectWithFieldsToValidate,
                                  requiredFields,
                                  stringLengthValidationCheck,
                                  emailValidation )

    let result = errorMessageBag(fields, linkmodel()) // That's the end result
    console.log( "result", result ) 
  })



  // Return object containing all fields to validate and their criterias
  function objectWithFieldsToValidate( fields, model ) {
    // Remove all model fields that have no validation criteria in model. Description is one of those.
    let modelFieldsToValidate = _.pickBy( model, function( value, key ) { return value.validation !== undefined })

    // Remove from form field any entry that doesn't have a corresponding key in model
    let formFieldsToValidate = _.pick( fields, Object.keys( modelFieldsToValidate ) )
    _.forOwn( modelFieldsToValidate, function( value1, key1 ) {
      _.forOwn( formFieldsToValidate, function( value, key ) {
        if ( key1 === key ) {
          modelFieldsToValidate[ key ].content = value
        }

      })
    })
    return modelFieldsToValidate
  }

  // Take care of required fields
  function requiredFields( objectWithFieldsToValidate ) {
    let okField = {}
    let errors = {}

    // error: field required but empty: add it to errors literal object
    _.forOwn( objectWithFieldsToValidate, function( value, key ) {
      if ( objectWithFieldsToValidate[ key ].validation.required === true && objectWithFieldsToValidate[ key ].content === "" ) {
        errors[ key ] = objectWithFieldsToValidate[ key ].validation.validationErrorMessage
      } else {
        // no error: add field to litteral okField
        okField[ key ] = value

      }
    })
    return ( { "okField": okField, "errors": errors })
  }


  function stringLengthValidationCheck( requiredFields ) {
    let validatedFields = requiredFields
    _.forOwn( validatedFields.okField, function( value, key ) {
      // Error: field length is not valid
      if ( !validator.isLength( value[ "content" ],
        { min: value[ "validation" ].minLength, max: value[ "validation" ].maxLength }
      ) ) {
        // Add error message to errors errors literal object
        validatedFields[ "errors" ][ key ] = value[ "validation" ].validationErrorMessage
        // Remove problematic field from okFields
        delete validatedFields[ "okField" ][ key ]
      }
    })

    return validatedFields
  }


  function emailValidation( stringLengthValidationCheck ) {
    let validatedFields = stringLengthValidationCheck
    _.forOwn( validatedFields.okField, function( value, key ) {
      // Error
      if ( value["validation"]["type"] === "email" && !validator.isEmail( value[ "content" ])) {
        // Add error message to errors
        validatedFields[ "errors" ][ key ] = value[ "validation" ].validationErrorMessage
        // Remove problematic field from okFields
        delete validatedFields[ "okField" ][ key ]
      }
    })

    return validatedFields
  }

3) 如果您找到改进此代码的方法,我将不胜感激,看看您能提出什么更好的功能重构,仍然使用 lodash。

【问题讨论】:

  • 这看起来像是codereview.stackexchange.com的问题
  • 函数被认为是纯函数,因为它们总是将相同的输入映射到相同的输出并且不改变全局状态。 modelFieldsToValidate 中的突变很好。

标签: javascript functional-programming lodash


【解决方案1】:

正如上面的评论所暗示的,特定的代码审查可能不是 SO 的主题,而是回答您问题的一般部分:是的,没有外部可见副作用的函数可以被认为是更广泛意义上的“功能” ---比如说,“行为功能”---即使它在内部使用了赋值。

这里的一个可能问题是如何验证副作用确实只是内部的,从外部看不到。

另一个理论上有趣的事实是,存在一些行为功能但可以通过(内部)分配实现的功能。有关详细信息,请参阅 the paper(如果您无法访问 ACM 数字图书馆中的论文,请尝试在 Web 上搜索论文的标题和/或作者)。

【讨论】:

    猜你喜欢
    • 2010-09-26
    • 2018-07-25
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 2021-10-22
    • 2023-03-30
    • 2011-11-16
    • 1970-01-01
    相关资源
    最近更新 更多