Skip to content

meetup 0.0.4b

gsantandrea edited this page Apr 11, 2020 · 6 revisions

The n-th extra meetup!

We kept working on purescript_first_steps, always following the book "Finding Success and Failure".

Mostly expanded Either validation with Data.Validation.Semigroup.V and played along to adapt functions returning Either to work with V.

Lifting is a technique that allows us to make a function work in a more generic environment:

myFunction <$> (liftedParameter1) <*> (liftedParameter2) <*> (liftedParameter3)
  • every parameter of the function is "lifted" (= made more generic, that means wrapped in a context)
  • with map (<$>) and apply (<*>) we "lift" the function itself
  • the final result is "lifted" (it is in the same type of context)

We call "applicative validation" approach when we use "lifting" to do a validation alongside the creation of a new value.

In particular we had some functions that validate arguments and return an Either:

requireAlphaNum' :: String -> Either Error String

(we use Either instead of Maybe because in this way we have more informations about the errors).

However doing applicative validation only on Either does not allows us to chain the errors, so we use a particular type called V, that does exactly that, it chains the errors. So:

  • our context is a "validation context"
  • the context is the same for every validation ( V), but every validation function returns a specific type (V String, V Int, ...), and the final result of the validation will be V User

V requires a Semigroup data structure, that is something appendable, to chain errors so we create a transformation function to put errors in an Array:

mapError :: (Either String String) -> (Either (Array String) String)

NOTE: there is also a variant of V that requires a Semiring, instead of a Semigroup (it allows more complex validations).

... and then we can lift our parameters! (V is also the name of the type constructor)

requireAlphaNum =  V <<< mapError <<< requireAlphaNum'    :: V (Array String) String

We can compose the validation functions on the same parameters with applySecond (*>):

validatePassword'' :: String -> V (Array String) String
validatePassword'' password = 
   cleanWhitespace password
   *> requireAlphaNum password
   *> checkPasswordLength password

And finally we can define our validation:

makeUser :: String -> String -> V (Array String) User
makeUser u p = UserConstructor <$> validateUsername u
                               <*> validatePassword'' p

If the validation is successful we have the following printed to the console output:

pure (Username: "username", Password: "password123")

otherwise we have the errors, chained:

invalid (["Non alphanum char present","Value too short"])

Clone this wiki locally