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