简体   繁体   中英

Functional programming and local function state assignments

I recently converted a code to what I think is partly at least, functional javascript. It's a web form validation process. At each step of the process, the functions perform their own layer of validation and modify the data before passing it to the next function.

1) My first question is... can the code below be considered at least 'partly' functional ?

You will notice that in requiredFields I created two assignment... states. They are local to the function, and the function doesn't have external side effect....

2) Is this kind of local assignement bad practice in the context of functional programming paradigm ? Is this considered a 'local side effect ?'

Here is the model:

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

And here is the validation system:

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) If you see way to improve this code, I would appreciate to see what a better functional refactoring you can come up with, still using lodash.

As a comment above suggests, specific code review may not be a topic for SO, but to answer the general part of your question: yes, a function with no externally visible side effect can be considered "functional" in a broader sense---say, "behaviorally functional"---even if it internally uses assignments.

A possible problem here may be how to verify that the side effects are indeed internal only and are not visible from the outside.

Another, theoretically interesting fact is that there exist some functions that are behaviorally functional but can be implemented only with (internal) assignments. See the paper for more details (if you have no access to papers in the ACM Digital Library, try searching the Web for the title and/or author of the paper).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM