· 6 years ago · Jul 25, 2019, 03:02 AM
1// This sample will guide you through elements of the F# language.
2//
3// *******************************************************************************************************
4// To execute the code in F# Interactive, highlight a section of code and press Alt-Enter or right-click
5// and select "Execute in Interactive". You can open the F# Interactive Window from the "View" menu.
6// *******************************************************************************************************
7//
8// For more about F#, see:
9// http://fsharp.org
10// https://docs.microsoft.com/en-us/dotnet/articles/fsharp/
11//
12// To see this tutorial in documentation form, see:
13// https://docs.microsoft.com/en-us/dotnet/articles/fsharp/tour
14//
15// To learn more about applied F# programming, use
16// http://fsharp.org/guides/enterprise/
17// http://fsharp.org/guides/cloud/
18// http://fsharp.org/guides/web/
19// http://fsharp.org/guides/data-science/
20//
21// To install the Visual F# Power Tools, use
22// 'Tools' --> 'Extensions and Updates' --> `Online` and search
23//
24// For additional templates to use with F#, see the 'Online Templates' in Visual Studio,
25// 'New Project' --> 'Online Templates'
26
27// F# supports three kinds of comments:
28
29// 1. Double-slash comments. These are used in most situations.
30(* 2. ML-style Block comments. These aren't used that often. *)
31/// 3. Triple-slash comments. These are used for documenting functions, types, and so on.
32/// They will appear as text when you hover over something which is decorated with these comments.
33///
34/// They also support .NET-style XML comments, which allow you to generate reference documentation,
35/// and they also allow editors (such as Visual Studio) to extract information from them.
36/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/xml-documentation
37
38
39// Open namespaces using the 'open' keyword.
40//
41// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/import-declarations-the-open-keyword
42open System
43
44
45/// A module is a grouping of F# code, such as values, types, and function values.
46/// Grouping code in modules helps keep related code together and helps avoid name conflicts in your program.
47///
48/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/modules
49module IntegersAndNumbers =
50
51 /// This is a sample integer.
52 let sampleInteger = 176
53
54 /// This is a sample floating point number.
55 let sampleDouble = 4.1
56
57 /// This computed a new number by some arithmetic. Numeric types are converted using
58 /// functions 'int', 'double' and so on.
59 let sampleInteger2 = (sampleInteger/4 + 5 - 7) * 4 + int sampleDouble
60
61 /// This is a list of the numbers from 0 to 99.
62 let sampleNumbers = [ 0 .. 99 ]
63
64 /// This is a list of all tuples containing all the numbers from 0 to 99 and their squares.
65 let sampleTableOfSquares = [ for i in 0 .. 99 -> (i, i*i) ]
66
67 // The next line prints a list that includes tuples, using '%A' for generic printing.
68 printfn "The table of squares from 0 to 99 is:\n%A" sampleTableOfSquares
69
70 // This is a sample integer with a type annotation
71 let sampleInteger3: int = 1
72
73
74/// Values in F# are immutable by default. They cannot be changed
75/// in the course of a program's execution unless explicitly marked as mutable.
76///
77/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/values/index#why-immutable
78module Immutability =
79
80 /// Binding a value to a name via 'let' makes it immutable.
81 ///
82 /// The second line of code fails to compile because 'number' is immutable and bound.
83 /// Re-defining 'number' to be a different value is not allowed in F#.
84 let number = 2
85 // let number = 3
86
87 /// A mutable binding. This is required to be able to mutate the value of 'otherNumber'.
88 let mutable otherNumber = 2
89
90 printfn "'otherNumber' is %d" otherNumber
91
92 // When mutating a value, use '<-' to assign a new value.
93 //
94 // You could not use '=' here for this purpose since it is used for equality
95 // or other contexts such as 'let' or 'module'
96 otherNumber <- otherNumber + 1
97
98 printfn "'otherNumber' changed to be %d" otherNumber
99
100
101/// Much of F# programming consists of defining functions that transform input data to produce
102/// useful results.
103///
104/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/functions/
105module BasicFunctions =
106
107 /// You use 'let' to define a function. This one accepts an integer argument and returns an integer.
108 /// Parentheses are optional for function arguments, except for when you use an explicit type annotation.
109 let sampleFunction1 x = x*x + 3
110
111 /// Apply the function, naming the function return result using 'let'.
112 /// The variable type is inferred from the function return type.
113 let result1 = sampleFunction1 4573
114
115 // This line uses '%d' to print the result as an integer. This is type-safe.
116 // If 'result1' were not of type 'int', then the line would fail to compile.
117 printfn "The result of squaring the integer 4573 and adding 3 is %d" result1
118
119 /// When needed, annotate the type of a parameter name using '(argument:type)'. Parentheses are required.
120 let sampleFunction2 (x:int) = 2*x*x - x/5 + 3
121
122 let result2 = sampleFunction2 (7 + 4)
123 printfn "The result of applying the 2nd sample function to (7 + 4) is %d" result2
124
125 /// Conditionals use if/then/elid/elif/else.
126 ///
127 /// Note that F# uses whitespace indentation-aware syntax, similar to languages like Python.
128 let sampleFunction3 x =
129 if x < 100.0 then
130 2.0*x*x - x/5.0 + 3.0
131 else
132 2.0*x*x + x/5.0 - 37.0
133
134 let result3 = sampleFunction3 (6.5 + 4.5)
135
136 // This line uses '%f' to print the result as a float. As with '%d' above, this is type-safe.
137 printfn "The result of applying the 3rd sample function to (6.5 + 4.5) is %f" result3
138
139
140/// Booleans are fundamental data types in F#. Here are some examples of Booleans and conditional logic.
141///
142/// To learn more, see:
143/// https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/primitive-types
144/// and
145/// https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/symbol-and-operator-reference/boolean-operators
146module Booleans =
147
148 /// Booleans values are 'true' and 'false'.
149 let boolean1 = true
150 let boolean2 = false
151
152 /// Operators on booleans are 'not', '&&' and '||'.
153 let boolean3 = not boolean1 && (boolean2 || false)
154
155 // This line uses '%b'to print a boolean value. This is type-safe.
156 printfn "The expression 'not boolean1 && (boolean2 || false)' is %b" boolean3
157
158
159/// Strings are fundamental data types in F#. Here are some examples of Strings and basic String manipulation.
160///
161/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/strings
162module StringManipulation =
163
164 /// Strings use double quotes.
165 let string1 = "Hello"
166 let string2 = "world"
167
168 /// Strings can also use @ to create a verbatim string literal.
169 /// This will ignore escape characters such as '\', '\n', '\t', etc.
170 let string3 = @"C:\Program Files\"
171
172 /// String literals can also use triple-quotes.
173 let string4 = """The computer said "hello world" when I told it to!"""
174
175 /// String concatenation is normally done with the '+' operator.
176 let helloWorld = string1 + " " + string2
177
178 // This line uses '%s' to print a string value. This is type-safe.
179 printfn "%s" helloWorld
180
181 /// Substrings use the indexer notation. This line extracts the first 7 characters as a substring.
182 /// Note that like many languages, Strings are zero-indexed in F#.
183 let substring = helloWorld.[0..6]
184 printfn "%s" substring
185
186
187/// Tuples are simple combinations of data values into a combined value.
188///
189/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/tuples
190module Tuples =
191
192 /// A simple tuple of integers.
193 let tuple1 = (1, 2, 3)
194
195 /// A function that swaps the order of two values in a tuple.
196 ///
197 /// F# Type Inference will automatically generalize the function to have a generic type,
198 /// meaning that it will work with any type.
199 let swapElems (a, b) = (b, a)
200
201 printfn "The result of swapping (1, 2) is %A" (swapElems (1,2))
202
203 /// A tuple consisting of an integer, a string,
204 /// and a double-precision floating point number.
205 let tuple2 = (1, "fred", 3.1415)
206
207 printfn "tuple1: %A\ttuple2: %A" tuple1 tuple2
208
209 /// A simple tuple of integers with a type annotation.
210 /// Type annotations for tuples use the * symbol to separate elements
211 let tuple3: int * int = (5, 9)
212
213 /// Tuples are normally objects, but they can also be represented as structs.
214 ///
215 /// These interoperate completely with structs in C# and Visual Basic.NET; however,
216 /// struct tuples are not implicitly convertable with object tuples (often called reference tuples).
217 ///
218 /// The second line below will fail to compile because of this. Uncomment it to see what happens.
219 let sampleStructTuple = struct (1, 2)
220 //let thisWillNotCompile: (int*int) = struct (1, 2)
221
222 // Although you cannot implicitly convert between struct tuples and reference tuples,
223 // you can explicitly convert via pattern matching, as demonstrated below.
224 let convertFromStructTuple (struct(a, b)) = (a, b)
225 let convertToStructTuple (a, b) = struct(a, b)
226
227 printfn "Struct Tuple: %A\nReference tuple made from the Struct Tuple: %A" sampleStructTuple (sampleStructTuple |> convertFromStructTuple)
228
229
230/// The F# pipe operators ('|>', '<|', etc.) and F# composition operators ('>>', '<<')
231/// are used extensively when processing data. These operators are themselves functions
232/// which make use of Partial Application.
233///
234/// To learn more about these operators, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/functions/#function-composition-and-pipelining
235/// To learn more about Partial Application, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/functions/#partial-application-of-arguments
236module PipelinesAndComposition =
237
238 /// Squares a value.
239 let square x = x * x
240
241 /// Adds 1 to a value.
242 let addOne x = x + 1
243
244 /// Tests if an integer value is odd via modulo.
245 let isOdd x = x % 2 <> 0
246
247 /// A list of 5 numbers. More on lists later.
248 let numbers = [ 1; 2; 3; 4; 5 ]
249
250 /// Given a list of integers, it filters out the even numbers,
251 /// squares the resulting odds, and adds 1 to the squared odds.
252 let squareOddValuesAndAddOne values =
253 let odds = List.filter isOdd values
254 let squares = List.map square odds
255 let result = List.map addOne squares
256 result
257
258 printfn "processing %A through 'squareOddValuesAndAddOne' produces: %A" numbers (squareOddValuesAndAddOne numbers)
259
260 /// A shorter way to write 'squareOddValuesAndAddOne' is to nest each
261 /// sub-result into the function calls themselves.
262 ///
263 /// This makes the function much shorter, but it's difficult to see the
264 /// order in which the data is processed.
265 let squareOddValuesAndAddOneNested values =
266 List.map addOne (List.map square (List.filter isOdd values))
267
268 printfn "processing %A through 'squareOddValuesAndAddOneNested' produces: %A" numbers (squareOddValuesAndAddOneNested numbers)
269
270 /// A preferred way to write 'squareOddValuesAndAddOne' is to use F# pipe operators.
271 /// This allows you to avoid creating intermediate results, but is much more readable
272 /// than nesting function calls like 'squareOddValuesAndAddOneNested'
273 let squareOddValuesAndAddOnePipeline values =
274 values
275 |> List.filter isOdd
276 |> List.map square
277 |> List.map addOne
278
279 printfn "processing %A through 'squareOddValuesAndAddOnePipeline' produces: %A" numbers (squareOddValuesAndAddOnePipeline numbers)
280
281 /// You can shorten 'squareOddValuesAndAddOnePipeline' by moving the second `List.map` call
282 /// into the first, using a Lambda Function.
283 ///
284 /// Note that pipelines are also being used inside the lambda function. F# pipe operators
285 /// can be used for single values as well. This makes them very powerful for processing data.
286 let squareOddValuesAndAddOneShorterPipeline values =
287 values
288 |> List.filter isOdd
289 |> List.map(fun x -> x |> square |> addOne)
290
291 printfn "processing %A through 'squareOddValuesAndAddOneShorterPipeline' produces: %A" numbers (squareOddValuesAndAddOneShorterPipeline numbers)
292
293 /// Lastly, you can eliminate the need to explicitly take 'values' in as a parameter by using '>>'
294 /// to compose the two core operations: filtering out even numbers, then squaring and adding one.
295 /// Likewise, the 'fun x -> ...' bit of the lambda expression is also not needed, because 'x' is simply
296 /// being defined in that scope so that it can be passed to a functional pipeline. Thus, '>>' can be used
297 /// there as well.
298 ///
299 /// The result of 'squareOddValuesAndAddOneComposition' is itself another function which takes a
300 /// list of integers as its input. If you execute 'squareOddValuesAndAddOneComposition' with a list
301 /// of integers, you'll notice that it produces the same results as previous functions.
302 ///
303 /// This is using what is known as function composition. This is possible because functions in F#
304 /// use Partial Application and the input and output types of each data processing operation match
305 /// the signatures of the functions we're using.
306 let squareOddValuesAndAddOneComposition =
307 List.filter isOdd >> List.map (square >> addOne)
308
309 printfn "processing %A through 'squareOddValuesAndAddOneComposition' produces: %A" numbers (squareOddValuesAndAddOneComposition numbers)
310
311
312/// Lists are ordered, immutable, singly-linked lists. They are eager in their evaluation.
313///
314/// This module shows various ways to generate lists and process lists with some functions
315/// in the 'List' module in the F# Core Library.
316///
317/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/lists
318module Lists =
319
320 /// Lists are defined using [ ... ]. This is an empty list.
321 let list1 = [ ]
322
323 /// This is a list with 3 elements. ';' is used to separate elements on the same line.
324 let list2 = [ 1; 2; 3 ]
325
326 /// You can also separate elements by placing them on their own lines.
327 let list3 = [
328 1
329 2
330 3
331 ]
332
333 /// This is a list of integers from 1 to 1000
334 let numberList = [ 1 .. 1000 ]
335
336 /// Lists can also be generated by computations. This is a list containing
337 /// all the days of the year.
338 let daysList =
339 [ for month in 1 .. 12 do
340 for day in 1 .. System.DateTime.DaysInMonth(2017, month) do
341 yield System.DateTime(2017, month, day) ]
342
343 // Print the first 5 elements of 'daysList' using 'List.take'.
344 printfn "The first 5 days of 2017 are: %A" (daysList |> List.take 5)
345
346 /// Computations can include conditionals. This is a list containing the tuples
347 /// which are the coordinates of the black squares on a chess board.
348 let blackSquares =
349 [ for i in 0 .. 7 do
350 for j in 0 .. 7 do
351 if (i+j) % 2 = 1 then
352 yield (i, j) ]
353
354 /// Lists can be transformed using 'List.map' and other functional programming combinators.
355 /// This definition produces a new list by squaring the numbers in numberList, using the pipeline
356 /// operator to pass an argument to List.map.
357 let squares =
358 numberList
359 |> List.map (fun x -> x*x)
360
361 /// There are many other list combinations. The following computes the sum of the squares of the
362 /// numbers divisible by 3.
363 let sumOfSquares =
364 numberList
365 |> List.filter (fun x -> x % 3 = 0)
366 |> List.sumBy (fun x -> x * x)
367
368 printfn "The sum of the squares of numbers up to 1000 that are divisible by 3 is: %d" sumOfSquares
369
370
371/// Arrays are fixed-size, mutable collections of elements of the same type.
372///
373/// Although they are similar to Lists (they support enumeration and have similar combinators for data processing),
374/// they are generally faster and support fast random access. This comes at the cost of being less safe by being mutable.
375///
376/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/arrays
377module Arrays =
378
379 /// This is The empty array. Note that the syntax is similar to that of Lists, but uses `[| ... |]` instead.
380 let array1 = [| |]
381
382 /// Arrays are specified using the same range of constructs as lists.
383 let array2 = [| "hello"; "world"; "and"; "hello"; "world"; "again" |]
384
385 /// This is an array of numbers from 1 to 1000.
386 let array3 = [| 1 .. 1000 |]
387
388 /// This is an array containing only the words "hello" and "world".
389 let array4 =
390 [| for word in array2 do
391 if word.Contains("l") then
392 yield word |]
393
394 /// This is an array initialized by index and containing the even numbers from 0 to 2000.
395 let evenNumbers = Array.init 1001 (fun n -> n * 2)
396
397 /// Sub-arrays are extracted using slicing notation.
398 let evenNumbersSlice = evenNumbers.[0..500]
399
400 /// You can loop over arrays and lists using 'for' loops.
401 for word in array4 do
402 printfn "word: %s" word
403
404 // You can modify the contents of an an array element by using the left arrow assignment operator.
405 //
406 // To learn more about this operator, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/values/index#mutable-variables
407 array2.[1] <- "WORLD!"
408
409 /// You can transform arrays using 'Array.map' and other functional programming operations.
410 /// The following calculates the sum of the lengths of the words that start with 'h'.
411 let sumOfLengthsOfWords =
412 array2
413 |> Array.filter (fun x -> x.StartsWith "h")
414 |> Array.sumBy (fun x -> x.Length)
415
416 printfn "The sum of the lengths of the words in Array 2 is: %d" sumOfLengthsOfWords
417
418
419/// Sequences are a logical series of elements, all of the same type. These are a more general type than Lists and Arrays.
420///
421/// Sequences are evaluated on-demand and are re-evaluated each time they are iterated.
422/// An F# sequence is an alias for a .NET System.Collections.Generic.IEnumerable<'T>.
423///
424/// Sequence processing functions can be applied to Lists and Arrays as well.
425///
426/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/sequences
427module Sequences =
428
429 /// This is the empty sequence.
430 let seq1 = Seq.empty
431
432 /// This a sequence of values.
433 let seq2 = seq { yield "hello"; yield "world"; yield "and"; yield "hello"; yield "world"; yield "again" }
434
435 /// This is an on-demand sequence from 1 to 1000.
436 let numbersSeq = seq { 1 .. 1000 }
437
438 /// This is a sequence producing the words "hello" and "world"
439 let seq3 =
440 seq { for word in seq2 do
441 if word.Contains("l") then
442 yield word }
443
444 /// This sequence producing the even numbers up to 2000.
445 let evenNumbers = Seq.init 1001 (fun n -> n * 2)
446
447 let rnd = System.Random()
448
449 /// This is an infinite sequence which is a random walk.
450 /// This example uses yield! to return each element of a subsequence.
451 let rec randomWalk x =
452 seq { yield x
453 yield! randomWalk (x + rnd.NextDouble() - 0.5) }
454
455 /// This example shows the first 100 elements of the random walk.
456 let first100ValuesOfRandomWalk =
457 randomWalk 5.0
458 |> Seq.truncate 100
459 |> Seq.toList
460
461 printfn "First 100 elements of a random walk: %A" first100ValuesOfRandomWalk
462
463
464/// Recursive functions can call themselves. In F#, functions are only recursive
465/// when declared using 'let rec'.
466///
467/// Recursion is the preferred way to process sequences or collections in F#.
468///
469/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/functions/index#recursive-functions
470module RecursiveFunctions =
471
472 /// This example shows a recursive function that computes the factorial of an
473 /// integer. It uses 'let rec' to define a recursive function.
474 let rec factorial n =
475 if n = 0 then 1 else n * factorial (n-1)
476
477 printfn "Factorial of 6 is: %d" (factorial 6)
478
479 /// Computes the greatest common factor of two integers.
480 ///
481 /// Since all of the recursive calls are tail calls,
482 /// the compiler will turn the function into a loop,
483 /// which improves performance and reduces memory consumption.
484 let rec greatestCommonFactor a b =
485 if a = 0 then b
486 elif a < b then greatestCommonFactor a (b - a)
487 else greatestCommonFactor (a - b) b
488
489 printfn "The Greatest Common Factor of 300 and 620 is %d" (greatestCommonFactor 300 620)
490
491 /// This example computes the sum of a list of integers using recursion.
492 let rec sumList xs =
493 match xs with
494 | [] -> 0
495 | y::ys -> y + sumList ys
496
497 /// This makes 'sumList' tail recursive, using a helper function with a result accumulator.
498 let rec private sumListTailRecHelper accumulator xs =
499 match xs with
500 | [] -> accumulator
501 | y::ys -> sumListTailRecHelper (accumulator+y) ys
502
503 /// This invokes the tail recursive helper function, providing '0' as a seed accumulator.
504 /// An approach like this is common in F#.
505 let sumListTailRecursive xs = sumListTailRecHelper 0 xs
506
507 let oneThroughTen = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
508
509 printfn "The sum 1-10 is %d" (sumListTailRecursive oneThroughTen)
510
511
512/// Records are an aggregate of named values, with optional members (such as methods).
513/// They are immutable and have structural equality semantics.
514///
515/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/records
516module RecordTypes =
517
518 /// This example shows how to define a new record type.
519 type ContactCard =
520 { Name : string
521 Phone : string
522 Verified : bool }
523
524 /// This example shows how to instantiate a record type.
525 let contact1 =
526 { Name = "Alf"
527 Phone = "(206) 555-0157"
528 Verified = false }
529
530 /// You can also do this on the same line with ';' separators.
531 let contactOnSameLine = { Name = "Alf"; Phone = "(206) 555-0157"; Verified = false }
532
533 /// This example shows how to use "copy-and-update" on record values. It creates
534 /// a new record value that is a copy of contact1, but has different values for
535 /// the 'Phone' and 'Verified' fields.
536 ///
537 /// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/copy-and-update-record-expressions
538 let contact2 =
539 { contact1 with
540 Phone = "(206) 555-0112"
541 Verified = true }
542
543 /// This example shows how to write a function that processes a record value.
544 /// It converts a 'ContactCard' object to a string.
545 let showContactCard (c: ContactCard) =
546 c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "")
547
548 printfn "Alf's Contact Card: %s" (showContactCard contact1)
549
550 /// This is an example of a Record with a member.
551 type ContactCardAlternate =
552 { Name : string
553 Phone : string
554 Address : string
555 Verified : bool }
556
557 /// Members can implement object-oriented members.
558 member this.PrintedContactCard =
559 this.Name + " Phone: " + this.Phone + (if not this.Verified then " (unverified)" else "") + this.Address
560
561 let contactAlternate =
562 { Name = "Alf"
563 Phone = "(206) 555-0157"
564 Verified = false
565 Address = "111 Alf Street" }
566
567 // Members are accessed via the '.' operator on an instantiated type.
568 printfn "Alf's alternate contact card is %s" contactAlternate.PrintedContactCard
569
570 /// Records can also be represented as structs via the 'Struct' attribute.
571 /// This is helpful in situations where the performance of structs outweighs
572 /// the flexibility of reference types.
573 [<Struct>]
574 type ContactCardStruct =
575 { Name : string
576 Phone : string
577 Verified : bool }
578
579
580/// Discriminated Unions (DU for short) are values which could be a number of named forms or cases.
581/// Data stored in DUs can be one of several distinct values.
582///
583/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/discriminated-unions
584module DiscriminatedUnions =
585
586 /// The following represents the suit of a playing card.
587 type Suit =
588 | Hearts
589 | Clubs
590 | Diamonds
591 | Spades
592
593 /// A Disciminated Union can also be used to represent the rank of a playing card.
594 type Rank =
595 /// Represents the rank of cards 2 .. 10
596 | Value of int
597 | Ace
598 | King
599 | Queen
600 | Jack
601
602 /// Discriminated Unions can also implement object-oriented members.
603 static member GetAllRanks() =
604 [ yield Ace
605 for i in 2 .. 10 do yield Value i
606 yield Jack
607 yield Queen
608 yield King ]
609
610 /// This is a record type that combines a Suit and a Rank.
611 /// It's common to use both Records and Disciminated Unions when representing data.
612 type Card = { Suit: Suit; Rank: Rank }
613
614 /// This computes a list representing all the cards in the deck.
615 let fullDeck =
616 [ for suit in [ Hearts; Diamonds; Clubs; Spades] do
617 for rank in Rank.GetAllRanks() do
618 yield { Suit=suit; Rank=rank } ]
619
620 /// This example converts a 'Card' object to a string.
621 let showPlayingCard (c: Card) =
622 let rankString =
623 match c.Rank with
624 | Ace -> "Ace"
625 | King -> "King"
626 | Queen -> "Queen"
627 | Jack -> "Jack"
628 | Value n -> string n
629 let suitString =
630 match c.Suit with
631 | Clubs -> "clubs"
632 | Diamonds -> "diamonds"
633 | Spades -> "spades"
634 | Hearts -> "hearts"
635 rankString + " of " + suitString
636
637 /// This example prints all the cards in a playing deck.
638 let printAllCards() =
639 for card in fullDeck do
640 printfn "%s" (showPlayingCard card)
641
642 // Single-case DUs are often used for domain modeling. This can buy you extra type safety
643 // over primitive types such as strings and ints.
644 //
645 // Single-case DUs cannot be implicitly converted to or from the type they wrap.
646 // For example, a function which takes in an Address cannot accept a string as that input,
647 // or vive/versa.
648 type Address = Address of string
649 type Name = Name of string
650 type SSN = SSN of int
651
652 // You can easily instantiate a single-case DU as follows.
653 let address = Address "111 Alf Way"
654 let name = Name "Alf"
655 let ssn = SSN 1234567890
656
657 /// When you need the value, you can unwrap the underlying value with a simple function.
658 let unwrapAddress (Address a) = a
659 let unwrapName (Name n) = n
660 let unwrapSSN (SSN s) = s
661
662 // Printing single-case DUs is simple with unwrapping functions.
663 printfn "Address: %s, Name: %s, and SSN: %d" (address |> unwrapAddress) (name |> unwrapName) (ssn |> unwrapSSN)
664
665 /// Disciminated Unions also support recursive definitions.
666 ///
667 /// This represents a Binary Search Tree, with one case being the Empty tree,
668 /// and the other being a Node with a value and two subtrees.
669 type BST<'T> =
670 | Empty
671 | Node of value:'T * left: BST<'T> * right: BST<'T>
672
673 /// Check if an item exists in the binary search tree.
674 /// Searches recursively using Pattern Matching. Returns true if it exists; otherwise, false.
675 let rec exists item bst =
676 match bst with
677 | Empty -> false
678 | Node (x, left, right) ->
679 if item = x then true
680 elif item < x then (exists item left) // Check the left subtree.
681 else (exists item right) // Check the right subtree.
682
683 /// Inserts an item in the Binary Search Tree.
684 /// Finds the place to insert recursively using Pattern Matching, then inserts a new node.
685 /// If the item is already present, it does not insert anything.
686 let rec insert item bst =
687 match bst with
688 | Empty -> Node(item, Empty, Empty)
689 | Node(x, left, right) as node ->
690 if item = x then node // No need to insert, it already exists; return the node.
691 elif item < x then Node(x, insert item left, right) // Call into left subtree.
692 else Node(x, left, insert item right) // Call into right subtree.
693
694 /// Discriminated Unions can also be represented as structs via the 'Struct' attribute.
695 /// This is helpful in situations where the performance of structs outweighs
696 /// the flexibility of reference types.
697 ///
698 /// However, there are two important things to know when doing this:
699 /// 1. A struct DU cannot be recursively-defined.
700 /// 2. A struct DU must have unique names for each of its cases.
701 [<Struct>]
702 type Shape =
703 | Circle of radius: float
704 | Square of side: float
705 | Triangle of height: float * width: float
706
707
708/// Pattern Matching is a feature of F# that allows you to utilize Patterns,
709/// which are a way to compare data with a logical structure or structures,
710/// decompose data into constituent parts, or extract information from data in various ways.
711/// You can then dispatch on the "shape" of a pattern via Pattern Matching.
712///
713/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/pattern-matching
714module PatternMatching =
715
716 /// A record for a person's first and last name
717 type Person = {
718 First : string
719 Last : string
720 }
721
722 /// A Discriminated Union of 3 different kinds of employees
723 type Employee =
724 | Engineer of engineer: Person
725 | Manager of manager: Person * reports: List<Employee>
726 | Executive of executive: Person * reports: List<Employee> * assistant: Employee
727
728 /// Count everyone underneath the employee in the management hierarchy,
729 /// including the employee.
730 let rec countReports(emp : Employee) =
731 1 + match emp with
732 | Engineer(id) ->
733 0
734 | Manager(id, reports) ->
735 reports |> List.sumBy countReports
736 | Executive(id, reports, assistant) ->
737 (reports |> List.sumBy countReports) + countReports assistant
738
739
740 /// Find all managers/executives named "Dave" who do not have any reports.
741 /// This uses the 'function' shorthand to as a lambda expression.
742 let rec findDaveWithOpenPosition(emps : List<Employee>) =
743 emps
744 |> List.filter(function
745 | Manager({First = "Dave"}, []) -> true // [] matches an empty list.
746 | Executive({First = "Dave"}, [], _) -> true
747 | _ -> false) // '_' is a wildcard pattern that matches anything.
748 // This handles the "or else" case.
749
750 /// You can also use the shorthand function construct for pattern matching,
751 /// which is useful when you're writing functions which make use of Partial Application.
752 let private parseHelper f = f >> function
753 | (true, item) -> Some item
754 | (false, _) -> None
755
756 let parseDateTimeOffset = parseHelper DateTimeOffset.TryParse
757
758 let result = parseDateTimeOffset "1970-01-01"
759 match result with
760 | Some dto -> printfn "It parsed!"
761 | None -> printfn "It didn't parse!"
762
763 // Define some more functions which parse with the helper function.
764 let parseInt = parseHelper Int32.TryParse
765 let parseDouble = parseHelper Double.TryParse
766 let parseTimeSpan = parseHelper TimeSpan.TryParse
767
768 // Active Patterns are another powerful construct to use with pattern matching.
769 // They allow you to partition input data into custom forms, decomposing them at the pattern match call site.
770 //
771 // To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/active-patterns
772 let (|Int|_|) = parseInt
773 let (|Double|_|) = parseDouble
774 let (|Date|_|) = parseDateTimeOffset
775 let (|TimeSpan|_|) = parseTimeSpan
776
777 /// Pattern Matching via 'function' keyword and Active Patterns often looks like this.
778 let printParseResult = function
779 | Int x -> printfn "%d" x
780 | Double x -> printfn "%f" x
781 | Date d -> printfn "%s" (d.ToString())
782 | TimeSpan t -> printfn "%s" (t.ToString())
783 | _ -> printfn "Nothing was parse-able!"
784
785 // Call the printer with some different values to parse.
786 printParseResult "12"
787 printParseResult "12.045"
788 printParseResult "12/28/2016"
789 printParseResult "9:01PM"
790 printParseResult "banana!"
791
792
793/// Option values are any kind of value tagged with either 'Some' or 'None'.
794/// They are used extensively in F# code to represent the cases where many other
795/// languages would use null references.
796///
797/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/options
798module OptionValues =
799
800 /// First, define a zipcode defined via Single-case Discriminated Union.
801 type ZipCode = ZipCode of string
802
803 /// Next, define a type where the ZipCode is optional.
804 type Customer = { ZipCode: ZipCode option }
805
806 /// Next, define an interface type that represents an object to compute the shipping zone for the customer's zip code,
807 /// given implementations for the 'getState' and 'getShippingZone' abstract methods.
808 type ShippingCalculator =
809 abstract GetState : ZipCode -> string option
810 abstract GetShippingZone : string -> int
811
812 /// Next, calculate a shipping zone for a customer using a calculator instance.
813 /// This uses combinators in the Option module to allow a functional pipeline for
814 /// transforming data with Optionals.
815 let CustomerShippingZone (calculator: ShippingCalculator, customer: Customer) =
816 customer.ZipCode
817 |> Option.bind calculator.GetState
818 |> Option.map calculator.GetShippingZone
819
820
821/// Units of measure are a way to annotate primitive numeric types in a type-safe way.
822/// You can then perform type-safe arithmetic on these values.
823///
824/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/units-of-measure
825module UnitsOfMeasure =
826
827 /// First, open a collection of common unit names
828 open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
829
830 /// Define a unitized constant
831 let sampleValue1 = 1600.0<meter>
832
833 /// Next, define a new unit type
834 [<Measure>]
835 type mile =
836 /// Conversion factor mile to meter.
837 static member asMeter = 1609.34<meter/mile>
838
839 /// Define a unitized constant
840 let sampleValue2 = 500.0<mile>
841
842 /// Compute metric-system constant
843 let sampleValue3 = sampleValue2 * mile.asMeter
844
845 // Values using Units of Measure can be used just like the primitive numeric type for things like printing.
846 printfn "After a %f race I would walk %f miles which would be %f meters" sampleValue1 sampleValue2 sampleValue3
847
848
849/// Classes are a way of defining new object types in F#, and support standard Object-oriented constructs.
850/// They can have a variety of members (methods, properties, events, etc.)
851///
852/// To learn more about Classes, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/classes
853///
854/// To learn more about Members, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/members
855module DefiningClasses =
856
857 /// A simple two-dimensional Vector class.
858 ///
859 /// The class's constructor is on the first line,
860 /// and takes two arguments: dx and dy, both of type 'double'.
861 type Vector2D(dx : double, dy : double) =
862
863 /// This internal field stores the length of the vector, computed when the
864 /// object is constructed
865 let length = sqrt (dx*dx + dy*dy)
866
867 // 'this' specifies a name for the object's self identifier.
868 // In instance methods, it must appear before the member name.
869 member this.DX = dx
870
871 member this.DY = dy
872
873 member this.Length = length
874
875 /// This member is a method. The previous members were properties.
876 member this.Scale(k) = Vector2D(k * this.DX, k * this.DY)
877
878 /// This is how you instantiate the Vector2D class.
879 let vector1 = Vector2D(3.0, 4.0)
880
881 /// Get a new scaled vector object, without modifying the original object.
882 let vector2 = vector1.Scale(10.0)
883
884 printfn "Length of vector1: %f\nLength of vector2: %f" vector1.Length vector2.Length
885
886
887/// Generic classes allow types to be defined with respect to a set of type parameters.
888/// In the following, 'T is the type parameter for the class.
889///
890/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/generics/
891module DefiningGenericClasses =
892
893 type StateTracker<'T>(initialElement: 'T) =
894
895 /// This internal field store the states in a list.
896 let mutable states = [ initialElement ]
897
898 /// Add a new element to the list of states.
899 member this.UpdateState newState =
900 states <- newState :: states // use the '<-' operator to mutate the value.
901
902 /// Get the entire list of historical states.
903 member this.History = states
904
905 /// Get the latest state.
906 member this.Current = states.Head
907
908 /// An 'int' instance of the state tracker class. Note that the type parameter is inferred.
909 let tracker = StateTracker 10
910
911 // Add a state
912 tracker.UpdateState 17
913
914
915/// Interfaces are object types with only 'abstract' members.
916/// Object types and object expressions can implement interfaces.
917///
918/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/interfaces
919module ImplementingInterfaces =
920
921 /// This is a type that implements IDisposable.
922 type ReadFile() =
923
924 let file = new System.IO.StreamReader("readme.txt")
925
926 member this.ReadLine() = file.ReadLine()
927
928 // This is the implementation of IDisposable members.
929 interface System.IDisposable with
930 member this.Dispose() = file.Close()
931
932
933 /// This is an object that implements IDisposable via an Object Expression
934 /// Unlike other languages such as C# or Java, a new type definition is not needed
935 /// to implement an interface.
936 let interfaceImplementation =
937 { new System.IDisposable with
938 member this.Dispose() = printfn "disposed" }
939
940
941/// The FSharp.Core library defines a range of parallel processing functions. Here
942/// you use some functions for parallel processing over arrays.
943///
944/// To learn more, see: https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/array.parallel-module-%5Bfsharp%5D
945module ParallelArrayProgramming =
946
947 /// First, an array of inputs.
948 let oneBigArray = [| 0 .. 100000 |]
949
950 // Next, define a functions that does some CPU intensive computation.
951 let rec computeSomeFunction x =
952 if x <= 2 then 1
953 else computeSomeFunction (x - 1) + computeSomeFunction (x - 2)
954
955 // Next, do a parallel map over a large input array.
956 let computeResults() =
957 oneBigArray
958 |> Array.Parallel.map (fun x -> computeSomeFunction (x % 20))
959
960 // Next, print the results.
961 printfn "Parallel computation results: %A" (computeResults())
962
963
964
965/// Events are a common idiom for .NET programming, especially with WinForms or WPF applications.
966///
967/// To learn more, see: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/members/events
968module Events =
969
970 /// First, create instance of Event object that consists of subscription point (event.Publish) and event trigger (event.Trigger).
971 let simpleEvent = new Event<int>()
972
973 // Next, add handler to the event.
974 simpleEvent.Publish.Add(
975 fun x -> printfn "this handler was added with Publish.Add: %d" x)
976
977 // Next, trigger the event.
978 simpleEvent.Trigger(5)
979
980 // Next, create an instance of Event that follows standard .NET convention: (sender, EventArgs).
981 let eventForDelegateType = new Event<EventHandler, EventArgs>()
982
983 // Next, add a handler for this new event.
984 eventForDelegateType.Publish.AddHandler(
985 EventHandler(fun _ _ -> printfn "this handler was added with Publish.AddHandler"))
986
987 // Next, trigger this event (note that sender argument should be set).
988 eventForDelegateType.Trigger(null, EventArgs.Empty)
989
990
991
992#if COMPILED
993module BoilerPlateForForm =
994 [<System.STAThread>]
995 do ()
996 do System.Windows.Forms.Application.Run()
997#endif