-
Notifications
You must be signed in to change notification settings - Fork 0
F# Mistakes
let retainPositive lst = List.collect (fun a -> if a > 0 then a else []) lst
retainPositive [ 1; -2; -4; 0; 11] // should return [1; 11]
- recording types of the function output and check if they are correct. - Ionide already somewhat does this?
let intList = [1..5]
let makePairs a0 = List.map (fun b -> (a0,b)) intList
let pairList = List.collect makePairs intList
// Use anon functions to avoid baked in values
let makePairs a0 = fun lst -> List.map (fun b -> (a0,b)) lst
let pairList = fun lst -> List.collect (fun a0 -> makePairs a0 lst) lst
Partially applied functions:
Think about which one will be most likely be (partially applied)[https://intranet.ee.ic.ac.uk/t.clarke/hlp/tutor1/A7.html] must be written as the first parameter
let intList = [1..5]
let makePairs lst = fun a -> List.map (fun x -> (a,x)) lst
let pairList = List.map (makePairs intList) intList
printfn "%A" pairList
let square (x) =
x*x
let makeTriple (a,b) = (square(a) - square(b),2 * a * b,square(a) + square(b))
let tripleList = List.map makeTriple pairList
printfn doesn't print, only outputs error?
let pairList = List.collect (fun a -> List.map (fun b -> (a,b)) [1..5]) [1..5]
let makeTriple (a,b) =
let sq x = x*x
(sq a - sq b, 2*a*b, sq a + sq b)
let tripleList = List.map makeTriple pairList
printfn "Triples are:%A" tripleList
System.Console.ReadLine() // prevent the program from terminating in Visual Studio
// versus
// this code is clever but not as easy to read as when makePairs is named
let pairListFn = fun lst -> List.collect (fun a -> List.map (fun x -> (a,x)) lst) lst
let makeTriple (a,b) =
let sq x = x*x
(sq a - sq b, 2*a*b, sq a + sq b)
let tripleList = List.map makeTriple (pairListFn [1..5])
printfn "Triples are:%A" tripleList
System.Console.ReadLine() // prevent the program from terminating[<fun:Invoke@2810>; <fun:Invoke@2810>; <fun:Invoke@2810>; <fun:Invoke@2810>;
<fun:Invoke@2810>; <fun:Invoke@2810>; <fun:Invoke@2810>; <fun:Invoke@2810>;
<fun:Invoke@2810>;
Simplify anon function
let singleList = fun a -> (a, makeTriple a)
let multiList = fun lst -> List.map (fun a -> singleList a) lst
//versus
let multiList pl = List.map (fun p -> p, makeTriple p) pl-
Empty list/array: [] is an empty list and also an empty list of lists, while [[]] is a list of lists containing only one empty list as its elements.
-
List/array delimiter: While ; is the list/array delimiter; , is the tuple delimiter. Therefore, [1, 3, 2, 4] is an (intintint*int) list with one element and [1; 3; 2; 4] is an int list with 4 elements.
-
Incomplete if/else expression: Any function returning unit can be an incomplete if/else statement: List.iter (fun i -> if i%2=0 then printfn "i=%i" i) [1; 3; 2; 4] However, other functions require a complete if...then...else... expression: List.map (fun i -> if i%2=0 then i+1 else i) [1; 3; 2; 4]
let func =
printfn "hi"
1
// vs.
let func() =
printfn "hi"
1
Sometimes, you want to define a function that doesn't take any input. You can't, however, define it like this:
let f = whateverbecause that would make it a value that's immediately let-bound to whatever. Instead, you can let the function take a value of the built-in type unit. This type only has a single value, which is written ():
let f () = whateverThis means that f is a function that pattern matches its input against the only known value of unit.
Whenever you invoke f with (), the expression whatever is evaluated and returned.
Another common error related to asynchronous workflows (especially when using MailboxProcessor) is to use do! to make recursive calls. The following code is wrong and it leaks memory with every loop:
let rec loop () = async {
do! Async.Sleep(1000)
do! loop() } // Wrong!
// In order to write tail-recursive calls in F# async workflows, we should use return!:
let rec loop () = async {
do! Async.Sleep(1000)
return! loop() } // Correct :-)The most common mistakes I see are generally related to scoping stuff incorrectly, generally creating scopes that just have too much stuff in them. Over use of shadowing, that is reusing an identifier name, can lead to confusing code. Generally this feature is fine if used in small scope, but can be very make code difficult to understand if used in a large scope. For example, if the dots are replaced with an arbitary amount of valid code the following example would be hard to follow as it would be difficult to know which version of myIdent is in scope:
let main() =
let myIdent = "something ..."
...
...
...
let myFunc() =
let myIdent = "something else ..."
...
...
myIdent
...
myIdenttype TickTack = Tick | Tack
let ticker x =
match x with
| Tick -> printfn "Tick"
| Tock -> printfn "Tock"
| Tack -> printfn "Tack"
ticker Tick
ticker TackOuput is
Tick
Tock
Instead of expected
Tick
Tack
While Tick and Tack are union cases of the type TickTack, Tock is a value of type TickTack.
When Tack is entered as an argument to ticker function pattern rule 1 does not match, but pattern rule 2 instead of any matching bind Tack to Tock then executes "Tock".
-
Research
-
Implementation
-
Weekly Reports
-
Meeting Feedback