In functional languages (using F #), I try my best to find a balance between the advantages of a functional composition with one responsibility and getting the performance of one iteration over sequences. Any suggestions / examples of code templates to achieve both?
I don’t have a solid background in computational theory, and again and again I come across this general pattern: Iterating over a collection and the desire to do side effects during iteration to avoid further iterations over the same collection or its set of results.
A typical example is the “reduce” or “filter” function: during filtering, there are many times that I want to take an extra step based on the result of the filter, but I would like to avoid a second listing of the filtered results.
Let the input check be performed as a simple task:
- Named input array
- Connected to isValid function filter
- Side effect: register invalid input names
- Entering the correct entries for further execution
Problem example
In F #, I could first write:
inputs
// how to log invalid or other side-effects without messing up isValid??
|> Seq.filter isValid
|> execution
Solution Example # 1
With a threaded side effect, I need something like:
inputs
|> Seq.filter (fun (name,value) ->
let valid = isValid (name,value)
if not valid then
printfn "Invalid argument %s" name
valid
|> execution
Solution Example 2
I could use tuples for a cleaner separation of problems, but requiring a second iteration:
let validationResults =
inputs
|> Seq.filter (fun (name,value) ->
let valid = isValid (name,value)
(name,value,valid)
|> execution
validationResults
|> Seq.filter (fun (_,_,valid) -> not valid)
|> Seq.map (fun (name,_,_) -> printfn "Invalid argument %s" name)
|> ignore
for validationResult in validationResults do
if not valid then
printfn "Invalid argument %s" name
Update 2014-07-23 per reply
. , . , , ...
open System
let inputs = [("name","my name");("number","123456");("invalid","")]
let isValidValue (name,value) =
not (String.IsNullOrWhiteSpace(value))
let logInvalidArg (name,value) =
printfn "Invalid argument %s" name
let execution (name,value) =
printfn "Valid argument %s: %s" name value
let inputPipeline input =
match isValidValue input with
| true -> execution input
| false -> logInvalidArg input
inputs |> Seq.iter inputPipeline