Functional-style control flow in F#
Lincoln Atkinson7/17/2013
Agenda
• Motivation/goals
• Idiomatic control-flow mechanisms in F#• Pattern matching/Active patterns• Loops via recursion• Higher-order functions for collection processing
• Examples
Motivation
• Most of us learn imperative-style control flow first
• F# allows for a fair bit of imperative style, but this is not always the best approach
• “Translate your C# projects to F#” is advice often given to those looking to learn F#
• Not bad advice, but naïve translations might leave a bad impression• These patterns usually require mutable state, extra local variables,
nested scopes…
FSM
FSM
(F# Spaghetti Monster)
Goals
• Help you have a better understanding of how to use common functional-style tools via F#, and how they compare to imperative-style tools
• Show you at least 1 thing about F# you hadn’t seen before, didn’t know before, or hadn’t thought about before
Non-Goals
• Teach you intro F#
• Convince you that functional-style is the only cool thing to do, or that imperative code is “bad”
Pattern Matching
• Powerful mechanism to partition data into distinct cases
• Encompasses capabilities of “switch” + “if/else” + more, all in one language construct
• Can pattern match against:• Constant literal values• Data structure (for common F# types)• Types• Arbitrary tests• Combinations of above
• Matching and capturing are done in a single step
(examples)
Active Patterns
• Allow for custom partitioning of data, beyond native pattern matching capabilities
• Enhance readability by keeping partitioning implementation details separate from usage
• Perfect for when the logic to determine cases is complex enough that ideas become obscured by the code. “This looks complex, but it’s really just a few fundamental cases.”
(examples)
Recursive loops
• F# supports basic imperative loops• ‘for’ with constant iterators• ‘foreach’ over collections• ‘while’
• Limitations• Loop bodies must return ‘unit’, so mutation required in order to obtain
computed data• No ‘break’ statement (yet)
Recursive loops - cont
• Advantages of recursive loops• More general approach – all loop types implemented essentially the
same via recursion• No mutation required• Arbitrary short-circuiting (i.e. ‘break) is simple to implement• Can return any data you want
• Disadvantages of recursive loops• Harder to grok at first• Slower
Recursive loops - cont
• General approach• Identify the stopping condition(s) of the loop, and what data affect
it(them).• Identify the data that are processed or mutated in each iteration• Define a recursive function which accepts arguments capturing each of
the above• In recursive function body
• Check stopping conditions and return if needed• Otherwise compute data for next iteration and pass recursively
(examples)
Higher-order functions
• Any function that either• Accepts another function as an argument, or• Returns a function as output
• Particularly useful for transforming and processing collections• Chaining (pipelining) such functions together is a common strategy
Higher-order functions - cont
• Key higher-order functions to know for collection processing• map – transform a collection to a new one by applying a function to each
element of the original
• fold – transform a collection into a single result by successively applying a function to each element, threading an accumulator through the calculation
• filter – transform a collection into a new one by applying a Boolean function to each element of the original and keeping only those which cause the function to return true
• iter – apply a given function to each element of a collection, returning unit.
(examples)
Takeaways
• Pattern matching can greatly streamline code, reduce spaghetti nested if-blocks, and simplify branching
• Active patterns can be used to enhance expressivity, readability, and modularity of complex partitioning logic
• Recursion is a powerful, general approach to looping
• Higher-order functions provide clean, pre-packaged engines for common actions
Questions?