In this lab you will extend the railway programming example from lecture to incorporate more validation functions and other patterns in functional design.
Recall the following terms from the Railway Programming lecture:
Two-track function: a two-track function accepts a ValidationResult object as its input (which has two tracks : Success and Failure), and produces a ValidationResult as its output. Two-track functions can be composed, because the output type of one matches the input type of
Switch function: a switch function accepts a RegistrationAttempt object as input and produces a ValidationResult It is like a railroad switch : one track comes in, two tracks go out. A switch function cannot be composed with other switch functions, because its output type (two-track) does not match its input type (one-track).
Bind function: the bind function wraps a switch function which normally expects a ValidationResult
inside of a two-track function called the bind. The bind accepts a two-track ValidationResult and if
the input is on the Success track, invokes the wrapped switch function and returns its ValidationResult (which could result in either Success or Failure). If the two-track input is on the Failure track, the bind just returns Failure. Binding a switch function produces a new function that can be composed with other two-track functions, because its two-track output type now matches its two-track input.
Figure 1: Example tracks. From left to right: a two-track function; a switch function; a binding over a switch function. In each, the upper track is success and the lower is failure. The gray concrete box encapsulates the function's body .
We will extend these function types with new track types:
One-track function: a one-track function accepts a RegistrationAttempt and returns a RegistrationAttempt. It does not do any validation; its purpose is to transform the input in some way, perhaps by lower-
casing the email address or otherwise manipulating the data to make it easier to validate later. These functions do not know how to handle Failure inputs; they can only be invoked on successful attempts. They can be composed with each other since the output type matches the input type but not with two-track functions.
Bypass function: a bypass function is a switch function that wraps a one-track function. The single- track input to the switch is passed to the one-track function, and the result is reported as Success. A
dummy Failure output track is never actually used, but gives the one-track function the same shape as a swich function, so it can then be binded into a two-track function.
Terminal function: a terminal function is a switch-type function that needs more parameters than just a RegistrationAttempt to do its validation. For example, to validate that an email address has not been used before, a function would need a parameter representing an existing set of user emails. A normal switch function can only take a single parameter (the registration attempt input); how then can we provide extra parameters to a switch function?
Lucky for us, F# makes this easy. If we write our terminal function such that the last parameter is the RegistrationAttempt input expected of a switch function, then we can wrap a binding around a partial invocation of the terminal, in which we call the terminal function and provide all but the last parameter. (Therefore passing the extra information needed by the terminal.) Such a partial terminal function call can be placed into a bind operation, including the >=> operator.
Figure 2: More track types. A bypass function over a single-track function; a terminal function with an extra parameter; a binding over the same terminal function, making it two-track.
Starting with the code given to you in the F# repository (RailwayProgramming), extend the registration validation example by following these instructions:
Create a list of strings named existingAccounts containing ve distinct (but fake) email addresses, none of which contains a period or
Create another list of strings named blacklistedDomains containing the values org and throwawaymail.com .
Write these validation functions:
A terminal function uniqueEmail, which takes a list of strings and a RegistrationAttempt as parameters, and validates that the attempt's email address is not in the list of strings. You cannot access the global list you created in step (1); you must use the parameter to the
A terminal function emailNotBlacklisted, which takes a list of strings and a RegistrationAttempt and validates that the domain of the email address (following the @) is not in the list of
Same rule as (a).
Both of these terminal functions can be added to your validation chain using partial invocation; simply adding >=> uniqueEmail myListOfEmails should do the trick.
Write helper functions to create a bypass over a single-track function:
Write the function bypass, which takes a single-track function and a RegistrationAttempt as parameters, invokes the single-track function on the RegistrationAttempt, and returns the result as a Success.
Write an operator >-> by mimicking the >=> operator, except that:
the second parameter is not a switch function, but a bypassFunction.
the bypassFunction needs to be promoted to switch using bypass before passing it to bind. Everything else in the operator stays the same.
DescriptionIn this final assignment, the students will demonstrate their ability to apply two ma
Path finding involves finding a path from A to B. Typically we want the path to have certain properties,such as being the shortest or to avoid going t
Develop a program to emulate a purchase transaction at a retail store. Thisprogram will have two classes, a LineItem class and a Transaction class. Th
1 Project 1 Introduction - the SeaPort Project series For this set of projects for the course, we wish to simulate some of the aspects of a number of
1 Project 2 Introduction - the SeaPort Project series For this set of projects for the course, we wish to simulate some of the aspects of a number of