This Note contains:
- Function Syntax Reminder, Function as Values, Function Scope
- Lambda Expression
- Currying
- Function Composition
- Building an app from functions
- Pattern Matching for structural decomposition
- Match Expressions: Advanced
- Functions Syntax Reminder
- Function Scope
- Functions as values
- Lambda Expression
- Currying
- Function Composition
- Building an app from functions
- Match Expression
- Pattern Matching for Structural Decomposition
- Exercise Quiz
Functions Syntax Reminder
1 | let cylinderVolume radius length = |
Declared with
let
keyword and a nameParameters added after a space, their type automatically inferred
Indentation mandatory to indicate a function body
Last expression evaluates to the return value
1 | let cylinderVolume radius length = |
Input / Output type
Type inference avoids the need to declare input / output types
To specify a return type, add a colon and the type after the parameters list
1 | let cylinderVolume radius length : float = |
For input parameters, encompass each om brackets and provide type after a colon.
1 | let cylinderVolume (radius : float) (length : float) : float = |
Local Identifier
It can contain definitions of of local identifies and functions
- Accessible only in scope of the body of the current function
1 | let cylinderVolume radius length = |
Function Scope
Names can be reused at any level other than module scope.
- Later declaration shadow previous one
1 | let list1 = [1;2;3] |
Error: duplicate definition
This code Error because Name reused in implicit top-level module
This code will run if we comment line 2 out and the function will return a empty records.
Functions as values
All function are considered values.
Function accept another function as parameter.
Function can return another function as the value of the function call.
High order function: a function that takes another function as a parameter, or a function that returns another function as a value, or a function that does both.
High order function example
1 | let twice f v = // this function will call f function twice with parameter v and (f v) |
Twice
is a higher order function because it accepts a function as parameter and returns a function
1 | let applyToPair f (x, y) = // parameter: f as function, (x, y) as tuple |
High Order Function: Key Take ways
- A frequently used technique to construct new function from existing functions and reuse them
- Allow to express algorithms and pass functionality as parameters
Lambda Expression
Lambda Expression is a function without a name (anonymous).
- It defined by using the
fun
keyword ->
separates the arguments from the function body
1 | fun x y -> x + y |
- Multi-line lambda are encompassed by paratheses
1 | (fun elem a -> |
- A lambda expression can be assigned to an identifier
1 | let square = fun x -> x * x |
Closure
A function using identifier from the lexical scope that is defined within.
1 | let square = fun x -> x * x |
- parameter of both square function and lambda expression *”close over”*
1 | let result = square 6 // function evaluation |
- The bidirectional interaction allow function to “remember” identifiers fron their environment when they were created.
Usage
Passed as argument to other function (sort, filter, …)
A function is used a limited number of times
Creation of small helper functions on the spot
Currying
A functional method of handling argument
Reminder:
- Function can accept only 1 parameter
- Function values can be returned from a function
Technique for simulating a multi-parameter function by sequentially returning different single parameter
Example #1
1 | let printTwoParameters x y = |
To , Translate the definition into one-parameterfunction.
Inside, construct a function that takes in the nex parameter.
1 | let printTwoParameter x = |
It return the newly created subfunction innerFun
.
Exercise #1
Re-write as a set of curried functions:
1 | let addThreeParameters x y z = |
Solution
1 | let addThreeParameters x = |
For both implementation, they have the same function signature.
Key takeaways
It ensure all F# functions accept only 1 input and evaluate to 1 output
It shows a lot of syntactic sugar exist in F#
Example #2
1 | let sum x y = |
- Fix the
x
parameter and varyy
only
1 | sum 2 3 |
- Write a function to make 2 a constant parameter
1 | let sumBy2 y = sum 2 y |
Wrapper Function
Example: creation of DateTime
Object
1 | open system |
- Function refactored to bake-in parameters
1 | open system |
Partial Application: the act of calling a curried function to get back a new function
Exercise #2
Create a wrapper function, writeToFile
, for writing data to a text file :
- The function should take in 3 argument in this specific order:
- date - The current date (
DateTime
type) - filename - a filename
- text - the text to write out
- date - The current date (
- The body should create a filename in the form
{date}-{filename}.txt
- Construct date part of the filename by using
ToString("yyMMdd")
Solution
1 | open System |
Step 2
Create more-constrained version of writeToFile
:
- a version of the function to print with today’s date (hint: use
DateTime.Today
) - a version of the function to print with a specific fixed file name and today’s date
- a version of function where only the text is fixed with “test”
Solution
1 | open System |
Function Composition
Function can be composed by *”gluing them together”* to create a new function
- given functions
a -> b
andb -> c
- generate a new function
a -> c
Compose >>
operater is used for this purpose
1 | let function1 x = x + 1 |
Types
Function can be composed as long as the output type of the first one is the same as the input type of the second one
1 | let f (x:int) = float x * 3.0 |
- Output of f is compatible with input of g hence functions can be composed
1 | let h = f >> g |
Backwards Compose
<<
operator composes two functions in reverse order
1 | let square x = x * x |
Function composition is a way to achieve information hiding in F#
Exercise
Which of the following lines will compile? If there is and issue, what is it?
1 | let add n x = x + n |
Solution
composed1
won’t compile because compare
is short of one parameter. times
only output one parameter
composed2
won’t compile because (add, times)
is one object as a tuple but 2 value expect
composed3
will compile which partial application makes this work
composed4
will compile and it’s equivalent of composed3
composed5
won’t compile because times
is lack of parameter and will evaluated first
Building an app from functions
Basic functions as building blocks
Composing multiple basic functions to service
Composing services into a business workflow
Composing workflows into an application
Match Expression
F# Example:
1 | let x = 2 |
Syntax
- The
match
keyword, preceding the expression to be evaluated - The
with
keyword, indicating start of the values to compare against - The vertical
pipe
character (|
) for every pattern that will be evaluated for a match - The arrow (
->
), preceding the expression to execute if the match is found - Pattern matching compares a test expression with a set of patterns
- Upon a match, the corresponding result-expression is evaluated.
- Resulting value is returned from the match expression. test expression
Expression
Expressions are constructs that evaluate to a value.
Statements are actions that a program takes.
Functional programming paradigm firmly encourages expressions as the default way of working
- No notion of void functions
- Branching mechanisms are expressions
Return Value
- A match expression always returns a value
- All clauses in the match expression must yield compatibly
Incomplete match expression will give a warnning
1 | let name = "Rafal" |
Wildcard Operator
“_” matches any value
Anything that doesn’t match against preceding clauses will match against this one
1 | let name = "Rafal” |
Pattern matching works top down. Put the most specific patterns first and the most general last.
Nested Match Expressions
Nesting match expressions introduces code complexity and should be used only for a significant number of repeated elements.
1 | let customer = ("good", 0) |
Guard
When clause (guard) allows to specify additional conditions in a match expression.
A guard allows to introduce any form of a check within a pattern rather than just matching against values
1 | let customer = ("good", 0) |
Comparing the bound values from a test expression.
1 | // comparing values in a when clause |
Testing properties as part of the when clause.
1 | let isAm aDate = |
When not to use when?
The compiler won’t figure out anything that happens inside the guard.
- Exhaustive pattern matching is not performed.
- One needs to define the default (wildcard operator) case.
Shortcut Syntax
Parameter and match .. with
can be omitted thanks to currying
1 | let getPrice1 food = |
Pattern Matching for Structural Decomposition
Extraction and binding of specific data from within a data structure.
Pattern matching is available for all the built-in F# types.
Tuple
“,
“ (comma) is needed to construct (and deconstruct) tuples
1 | let z = 1, true, "hello", 3.14 // "construct" a tuple |
- A new set of individual identifiers are bound to values from inside the tuple
“_
“ matches any input, but that the input is discarded instead of assigned to an identifier
1 | let z = 1, true, "hello", 3.14 |
Exercise
For the code snippet below write down patterns to extract :
- The value 3 from
a
and bind it to the identifiert
- The value 3 from
a
and true fromb
and bind them to the identifiersc
andd
respectively.
Implement it in 1 line (you can start with a 2-line notation first)
3. The values true
and 83
from b
and bind them to the identifiers e
and f
respectively
1 | let a = 2, 3 |
Solution:
1 | let _ , t = a // solution 1 |
Records
“=
“ sign is used to create record values
1 | type ComplexNumber = {Real: float; Imaginary: float} |
Construction and deconstruction syntaxes are Symmetric
1 | let myComplexNumber = { Real = 1.1; Imaginary = 2.2 } |
“_
“ discards part of a record one doesn’t need
1 | let {Real = a ; Imaginary = _} = myComplexNumber |
Alternatively, one can simply ignore an unwanted label from a Record
1 | let {Real = a} = myComplexNumber |
Discriminated Union
Use a “constructor” that refers to one of the possible union cases
1 | type CardType = Debit | Credit |
Each case must return the same data type (Payment)
1 | let deconstructor x = |
Evaluate “deconstructor” match expression to deconstruct a discriminated union
1 | let myPayment = CreditCard Debit // use “CreditCard” constructor |
A payment processing function that accepts Debit cards and cash only
1 | let processPayment payment = |
Exercise Quiz
In programming, assignment refers to the process of storing a value in a variable. It is typically performed using the assignment operator, represented by the equals sign (=).
In functional programming, binding is the action of linking an identifier and a value. In F# it is typically performed using the let keyword.
There are four important differences between “assignment” and pattern-matching & binding.
Please fill in the text below using the following options (each is used only once):
assignment
assignment
binding
shadowing
pattern-matching,
pattern-matching.
___ cannot fail
___ can
Assignment must assign a value to at least one variable. ___ does not necessarily result in any identifiers being bound.
___ always makes a copy
___ never causes any copies to be made
Re-assignment to an existing variable changes the value that is linked to that variable name. Rebinding a name does not affect any existing binding. What takes place is instead is ____.
This means that the new binding takes precedence over the existing one.
Answer
Assignment cannot fail.
Pattern-matching can.
Assignment must assign a value to at least one variable. Pattern-matching does not necessarily result in any identifiers being bound.
Assignment always makes a copy
Binding never causes any copies to be made
Re-assignment to an existing variable changes the value that is linked to that variable name. Rebinding a name does not affect any existing binding. What takes place is instead is Shadowing.
This means that the new binding takes precedence over the existing one.
- 本文作者: Depasinre
- 本文链接: https:/Depasinre.github.io/2024/01/24/18656-week2/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!