Created
March 28, 2017 17:19
-
-
Save colelawrence/1d1c7fb9a669ca2dfd545445e5599449 to your computer and use it in GitHub Desktop.
Practicing F-Sharp, very similar to Elixir concepts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #load "GetStarted1.fs" | |
| open GetStarted1 | |
| // https://fsharpforfunandprofit.com/posts/control-flow-expressions/ | |
| // https://docs.microsoft.com/en-us/dotnet/articles/fsharp/tour#pipelines-and-composition | |
| // Pattern matching examples | |
| let a,b = 1,2 | |
| type Person = {First:string; Last:string} | |
| let alice = {First="Alice"; Last="Doe"} | |
| let {First=first} = alice | |
| // | |
| // `use` is assoc with System.IDisposible compatible values. | |
| // After a use statement goes out of scope, the resource is disposed of. | |
| // This is brilliant, as it allows us to differentiate between long living and shared resources and to die resources with a single keyword difference. | |
| // create a new object that implements IDisposable | |
| let makeResource name = | |
| { new System.IDisposable | |
| with member this.Dispose() = printfn "%s disposed" name } | |
| // let returnInvalidResource name = | |
| // use myResource = makeResource name | |
| // myResource // don't do this! | |
| // // test | |
| // let resource = returnInvalidResource "hello" | |
| // | |
| // As always, you can force a non-unit result to be discarded by piping the results into “ignore”. | |
| do ( 1+1 |> ignore ) | |
| type Lang = | |
| | En | |
| | Es | |
| | Fr | |
| let GetGreeting lang = | |
| match lang with | |
| | En -> | |
| "Hello" | |
| | Es -> | |
| "Hola" | |
| | Fr -> | |
| "Bonjour" | |
| // Define your library scripting code here | |
| let Greet prefix = GetGreeting >> printfn "%s: %s, %s!" prefix | |
| let GreetEnglish = Greet "Hey" En | |
| // Similarly written to Java / C# | |
| let primesUpToVerbose n = | |
| // create a recursive intermediate function | |
| let rec sieve listOfNumbers = | |
| match listOfNumbers with | |
| | [] -> [] | |
| | primeP::sievedNumbersBiggerThanP-> | |
| let sievedNumbersNotDivisibleByP = | |
| sievedNumbersBiggerThanP | |
| |> List.filter (fun i-> i % primeP > 0) | |
| //recursive part | |
| let newPrimes = sieve sievedNumbersNotDivisibleByP | |
| primeP :: newPrimes | |
| // use the sieve | |
| let listOfNumbers = [2..n] | |
| sieve listOfNumbers // return | |
| // Here is the same implementation, with terser, idiomatic names and more compact code: | |
| let primesUpTo n = | |
| let rec sieve l = | |
| match l with | |
| | [] -> [] | |
| | p::xs -> | |
| p :: sieve [for x in xs do if (x % p) > 0 then yield x] | |
| [2..n] |> sieve | |
| // The common naming conventions are as follows: | |
| // - "a", "b", "c" etc., are types | |
| // - "f", "g", "h" etc., are functions | |
| // - "x", "y", "z" etc., are arguments to the functions | |
| // - Lists are indicated by adding an "s" suffix, so that | |
| // "xs" is a list of x's, "fs" is a list of functions, | |
| // and so on. It is extremely common to see "x::xs" | |
| // meaning the head (first element) and tail (the | |
| // remaining elements) of a list. | |
| // - "_" is used whenever you don’t care about the value. | |
| // So "x::_" means that you don't care about the rest | |
| // of the list, and "let f _ = something" means you | |
| // don't care about the argument to f. | |
| // Avoiding If-then-else | |
| // bad | |
| let f1 x = | |
| if x = 1 | |
| then "a" | |
| else "b" | |
| // not much better | |
| let f2 x = | |
| match x=1 with | |
| | true -> "a" | |
| | false -> "b" | |
| // best (direct matching) | |
| let f3 x = | |
| match x with | |
| | 1 -> "a" | |
| | _ -> "b" | |
| // Part of the reason why direct matching is better | |
| // is that the equality test throws away useful | |
| // information that you often need to retrieve again. | |
| // The first implementation does a test for empty and | |
| // then a second operation to get the first element. | |
| // A much better approach is to match and extract | |
| // the element in one single step, as shown in the | |
| // second implementation. | |
| // bad | |
| let fa list = | |
| if List.isEmpty list | |
| then printfn "is empty" | |
| else printfn "first element is %s" (List.head list) | |
| // much better | |
| let fb list = | |
| match list with | |
| | [] -> printfn "is empty" | |
| | x::_ -> printfn "first element is %s" x | |
| // Refactoring complex matching with `when` | |
| // bad | |
| let fc list = | |
| if List.isEmpty list | |
| then printfn "is empty" | |
| elif (List.head list) > 0 | |
| then printfn "first element is > 0" | |
| else printfn "first element is <= 0" | |
| // much better | |
| let fd list = | |
| match list with | |
| | [] -> printfn "is empty" | |
| | x::_ when x > 0 -> printfn "first element is > 0" | |
| | x::_ -> printfn "first element is <= 0" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment