1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407:
408:
409:
410:
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423:
424:
425:
426:
427:
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450:
451:
452:
453:
454:
455:
456:
457:
458:
459:
460:
461:
462:
463:
464:
465:
466:
467:
468:
469:
470:
471:
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482:
483:
484:
485:
486:
487:
488:
489:
490:
491:
492:
493:
494:
495:
496:
497:
498:
499:
500:
501:
502:
503:
504:
505:
506:
507:
508:
509:
510:
511:
512:
513:
514:
515:
516:
517:
518:
519:
520:
521:
522:
523:
524:
525:
526:
527:
528:
529:
530:
531:
532:
533:
534:
535:
536:
537:
538:
539:
540:
541:
542:
543:
544:
545:
546:
547:
548:
549:
550:
551:
552:
553:
554:
555:
556:
557:
558:
559:
560:
561:
562:
563:
564:
565:
566:
567:
568:
569:
570:
571:
572:
573:
574:
575:
576:
577:
578:
579:
580:
581:
582:
583:
584:
585:
586:
587:
588:
589:
590:
591:
592:
593:
594:
595:
596:
597:
598:
599:
600:
601:
602:
603:
604:
605:
606:
607:
608:
609:
610:
611:
612:
613:
614:
615:
616:
617:
618:
619:
620:
621:
622:
623:
624:
625:
626:
627:
628:
629:
630:
631:
632:
633:
634:
635:
636:
637:
638:
639:
640:
641:
642:
643:
644:
645:
646:
647:
648:
649:
650:
651:
652:
653:
654:
655:
656:
657:
658:
659:
660:
661:
662:
663:
664:
665:
666:
667:
668:
669:
670:
671:
672:
673:
674:
675:
676:
677:
678:
679:
680:
681:
682:
683:
684:
685:
686:
687:
688:
689:
690:
691:
692:
693:
694:
695:
696:
697:
698:
699:
700:
701:
702:
703:
704:
705:
706:
707:
708:
709:
710:
711:
712:
713:
714:
715:
716:
717:
718:
719:
720:
721:
722:
723:
724:
725:
726:
727:
728:
729:
730:
731:
732:
733:
734:
735:
736:
737:
738:
739:
740:
741:
742:
743:
744:
745:
746:
747:
748:
749:
750:
751:
752:
753:
754:
755:
756:
757:
758:
759:
760:
761:
762:
763:
764:
765:
766:
767:
768:
769:
770:
771:
772:
773:
774:
775:
776:
777:
778:
779:
780:
781:
782:
783:
784:
785:
786:
787:
788:
789:
790:
791:
792:
793:
794:
795:
796:
797:
798:
799:
800:
801:
802:
803:
804:
805:
806:
807:
808:
809:
810:
811:
812:
813:
814:
815:
816:
817:
818:
819:
820:
821:
822:
823:
824:
825:
826:
827:
828:
829:
830:
831:
832:
833:
834:
835:
836:
837:
838:
839:
840:
841:
842:
843:
844:
845:
846:
847:
848:
849:
850:
851:
852:
853:
854:
855:
856:
857:
858:
859:
860:
861:
862:
863:
864:
865:
866:
867:
868:
869:
870:
871:
872:
873:
874:
875:
876:
877:
878:
879:
880:
881:
882:
883:
884:
885:
886:
887:
888:
889:
890:
891:
892:
893:
894:
895:
896:
897:
898:
899:
900:
901:
902:
903:
904:
905:
906:
907:
908:
909:
910:
911:
912:
913:
914:
915:
916:
917:
918:
919:
920:
921:
922:
923:
924:
925:
926:
927:
928:
929:
930:
931:
932:
933:
934:
935:
|
//---------------------------------------------------------------
// Overview
//
// Below is a set of exercises designed to get you familiar
// with F#. By the time you're done, you'll have a basic
// understanding of the syntax of F# and learn a little more
// about functional programming in general.
//
// Answering Problems
//
// This is where the fun begins! Each dashed section contains an
// example designed to teach you a lesson about the F# language.
// If you highlight the code in an example and execute it (use
// Ctrl+Enter or the run button) it will initially fail. Your
// job is to fill in the blanks to make it pass. With each
// passing section, you'll learn more about F#, and add another
// weapon to your F# programming arsenal.
//
// Start by highlighitng the section below and running it. Once
// you see it fail, replace the __ with 2 to make it pass.
//---------------------------------------------------------------
// ---- about asserts -------------------------------------------
let expected_value = 1 + 1
let actual_value = __
AssertEquality expected_value actual_value
//Easy, right? Try the next one.
//---------------------------------------------------------------
// ---- more about asserts --------------------------------------
AssertEquality "foo" __
//---------------------------------------------------------------
//---------------------------------------------------------------
// About Let
//
// The let keyword is one of the most fundamental parts of F#.
// You'll use it in almost every line of F# code you write, so
// let's get to know it well! (no pun intended)
//---------------------------------------------------------------
// ---- let binds a name to a value -----------------------------
let x = 50
AssertEquality x __
//---------------------------------------------------------------
// ---- let infers the type of values when it can ---------------
(* In F#, values created with let are inferred to have a type like
"int" for integer values, "string" for text values, and "bool"
for true or false values. *)
let x = 50
let typeOfX = x.GetType()
AssertEquality typeOfX typeof<int>
let y = "a string"
let expectedType = y.GetType()
AssertEquality expectedType typeof<FILL_ME_IN>
//---------------------------------------------------------------
// ---- you can make the types explicit -------------------------
let (x:int) = 42
let typeOfX = x.GetType()
let y:string = "forty two"
let typeOfY = y.GetType()
AssertEquality typeOfX typeof<FILL_ME_IN>
AssertEquality typeOfY typeof<FILL_ME_IN>
(* You don't usually need to provide explicit type annotations
types for local varaibles, but type annotations can come in
handy in other contexts as you'll see later. *)
//---------------------------------------------------------------
// ---- modifying the value of variables ------------------------
let mutable x = 100
x <- 200
AssertEquality x __
//---------------------------------------------------------------
// ---- you can't modify a value if it isn't mutable ------------
let x = 50
//What happens if you try to uncomment and run the following line of code?
//(look at the output in the output window)
//x <- 100
//NOTE: Although you can't modify immutable values, it is
// possible to reuse the name of a value in some cases
// using "shadowing".
let x = 100
AssertEquality x __
//---------------------------------------------------------------
//---------------------------------------------------------------
// About Functions
//
// Now that you've seen how to bind a name to a value with let,
// you'll learn to use the let keyword to create functions.
//---------------------------------------------------------------
// ---- creating functions with let -----------------------------
(* By default, F# is whitespace sensitive. For functions, this
means that the last line of a function is its return value,
and the body of a function is denoted by indentation. *)
let add x y =
x + y
let result1 = add 2 2
let result2 = add 5 2
AssertEquality result1 __
AssertEquality result2 __
//---------------------------------------------------------------
// ---- nesting functions ---------------------------------------
let quadruple x =
let double x =
x * 2
double(double(x))
let result = quadruple 4
AssertEquality result __
//---------------------------------------------------------------
// ---- adding type annotations ---------------------------------
(* Sometimes you need to help F#'s type inference system out with
an explicit type annotation *)
let sayItLikeAnAuctioneer (text:string) =
text.Replace(" ", "")
let auctioneered = sayItLikeAnAuctioneer "going once going twice sold to the lady in red"
AssertEquality auctioneered __
//TRY IT: What happens if you remove the type annotation on text?
//---------------------------------------------------------------
// ---- using parenthesis to control the order of operation -----
let add x y =
x + y
let result = add (add 5 8) (add 1 1)
AssertEquality result __
(* TRY IT: What happens if you remove the parensthesis?*)
//---------------------------------------------------------------
//---------------------------------------------------------------
// Tuples
//
// Tuples are used to easily group together values in F#. They're
// another fundamental construct of the language.
//---------------------------------------------------------------
// ---- creating tuples -----
let items = ("apple", "dog")
AssertEquality items ("apple", __)
//---------------------------------------------------------------
// ---- accessing tuple elements --------------------------------
let items = ("apple", "dog", "Mustang")
let fruit, animal, car = items
AssertEquality fruit __
AssertEquality animal __
AssertEquality car __
(* Breaking apart tuples in this way uses a F# feature called
pattern matching. Pattern matching is another key concept in
F#, and you'll see more examples of it later on. *)
//---------------------------------------------------------------
// ---- ignoring values when pattern matching -------------------
let items = ("apple", "dog", "Mustang")
let _, animal, _ = items
AssertEquality animal __
//---------------------------------------------------------------
// ---- using tuples to return multiple values from a function --
let squareAndCube x =
(x ** 2.0, x ** 3.0)
let squared, cubed = squareAndCube 3.0
AssertEquality squared __
AssertEquality cubed __
//---------------------------------------------------------------
//---------------------------------------------------------------
// Branching
//
// Branching is used to tell a program to conditionally perform
// an operation. It's another fundamental part of F#.
//---------------------------------------------------------------
// ---- basic if statements -------------------------------------
let isEven x =
if x % 2 = 0 then
"it's even!"
else
"it's odd!"
let result = isEven 2
AssertEquality result __
//---------------------------------------------------------------
// ---- if statements return values -----------------------------
(* If you've worked with other programming languages, you might
be surprised to find out that if statements in F# return
values. *)
let result =
if 2 = 3 then
"something is REALLY wrong"
else
"math is workng!"
AssertEquality result __
//---------------------------------------------------------------
// ---- branching with pattern matching -------------------------
let isApple x =
match x with
| "apple" -> true
| _ -> false
let result1 = isApple "apple"
let result2 = isApple ""
AssertEquality result1 __
AssertEquality result2 __
//---------------------------------------------------------------
// ---- branching with tuples and pattern matching --------------
let getDinner x =
match x with
| (name, "veggies")
| (name, "fish")
| (name, "chicken") -> sprintf "%s doesn't want red meat" name
| (name, foodChoice) -> sprintf "%s wants 'em some %s" name foodChoice
let person1 = ("Bob", "fish")
let person2 = ("Sally", "Burger")
AssertEquality (getDinner person1) __
AssertEquality (getDinner person2) __
//---------------------------------------------------------------
//---------------------------------------------------------------
// Containers
//
// Containers are very important building blocks that you'll use
// in F# programming. They are used to group arbitrarily large
// sequences of values. It's very common to store values in a
// container and perform operations across each value in the
// sequence.
//---------------------------------------------------------------
//---------------------------------------------------------------
// Lists
//---------------------------------------------------------------
// ---- creating lists ------------------------------------------
let list = ["apple"; "pear"; "grape"; "peach"]
AssertEquality list.Head __
AssertEquality list.Tail __
AssertEquality list.Length __
//---------------------------------------------------------------
// ---- building new lists---------------------------------------
let first = ["grape"; "peach"]
let second = "pear" :: first
let third = "apple" :: second
//Note: "::" is known as "cons"
AssertEquality ["apple"; "pear"; "grape"; "peach"] third
AssertEquality second __
AssertEquality first __
//What happens if you uncomment the following?
//first.Head <- "apple"
//first.Tail <- ["peach"; "pear"]
//THINK ABOUT IT: Can you change the contents of a list once it
// has been created?
//---------------------------------------------------------------
// ---- creating lists with a range------------------------------
let list = [0..4]
AssertEquality list.Head __
AssertEquality list.Tail __
//---------------------------------------------------------------
// ---- creating lists with comprehensions-----------------------
let list = [for i in 0..4 do yield i ]
AssertEquality list __
//---------------------------------------------------------------
// ---- comprehensions with conditions --------------------------
let list = [for i in 0..10 do
if i % 2 = 0 then yield i ]
AssertEquality list __
//---------------------------------------------------------------
//---------------------------------------------------------------
// Arrays
//---------------------------------------------------------------
// ---- creating arrays -----------------------------------------
let fruits = [| "apple"; "pear"; "peach"|]
AssertEquality fruits.[0] __
AssertEquality fruits.[1] __
AssertEquality fruits.[2] __
//---------------------------------------------------------------
// ---- arrays are mutable --------------------------------------
let fruits = [| "apple"; "pear" |]
fruits.[1] <- "peach"
AssertEquality fruits __
//---------------------------------------------------------------
// ---- you can create arrays with comprehensions ---------------
let numbers =
[| for i in 0..10 do
if i % 2 = 0 then yield i |]
AssertEquality numbers __
//---------------------------------------------------------------
//---------------------------------------------------------------
// Operations on Sequences
//
// The following sections describe the Seq module which contains
// a bunch of functions for working with data inside of a
// collection. Note that any operations in the Seq module work
// on both arrays and lists.
//---------------------------------------------------------------
// ---- transforming collections with map -----------------------
let square x =
x * x
let original = [0..5]
let result = Seq.map square original
AssertEquality original __
AssertEquality result __
//---------------------------------------------------------------
// ---- filtering collections -----------------------------------
let isEven x =
x % 2 = 0
let original = [|0..5|]
let result = Seq.filter isEven original
AssertEquality original __
AssertEquality result __
//---------------------------------------------------------------
// ---- skipping elements ---------------------------------------
let original = [0..5]
let result = Seq.skip 2 original
AssertEquality result __
//---------------------------------------------------------------
// ---- finding the max -----------------------------------------
let values = [11; 20; 4; 2; 3]
let result = Seq.max values
AssertEquality result __
//---------------------------------------------------------------
// ---- finding the maximum using a condition -------------------
let getNameLength (name:string) =
name.Length
let names = [| "Harry"; "Lloyd"; "Nicholas"; "Mary"; "Joe"; |]
let result = Seq.maxBy getNameLength names
AssertEquality result __
(* Note: There are many other useful methods in the Seq module. Check them
via intellisense in Visual Studio by typing '.' after Seq, or online at
http://msdn.microsoft.com/en-us/library/ee353738.aspx *)
//---------------------------------------------------------------
//---------------------------------------------------------------
// Pipelining
//
// Now that you've seen a few operations for working with lists,
// you can combine them to do more interesting things
//---------------------------------------------------------------
// ---- squaring even numbers with separate statements ----------
let square x =
x * x
let isEven x =
x % 2 = 0
(* One way to combine the operations is by using separate statements.
However, this is can be clumsy since you have to name each result. *)
let numbers = [0..5]
let evens = List.filter isEven numbers
let result = List.map square evens
AssertEquality result __
//---------------------------------------------------------------
// ---- squaring even numbers with parens -----------------------
(* You can avoid this problem by using parens to pass the result of one
funciton to another. This can be difficult to read since you have to
start from the innermost function and work your way out. *)
let numbers = [0..5]
let result = List.map square (List.filter isEven numbers)
AssertEquality result __
//---------------------------------------------------------------
// ---- squaring even numbers with the pipeline operator --------
(* In F#, you can use the pipeline operator to get the benefit of the
parens style with the readablity of the statement style. *)
let result =
[0..5]
|> List.filter isEven
|> List.map square
AssertEquality result __
//---------------------------------------------------------------
// ---- using lambdas -------------------------------------------
let colors = ["maize"; "blue"]
let echo =
colors
|> List.map (fun x -> x + " " + x)
AssertEquality echo __
(* The fun keyword allows you to create a function inline without giving
it a name. These functions are known as anonymous functions, lambdas,
or lambda functions. *)
//---------------------------------------------------------------
//---------------------------------------------------------------
// Apply Your Knowledge!
//
// Below is a list containing comma separated data about
// Microsoft's stock prices during March of 2012. Without
// modifying the list, programatically find the day with the
// greatest variance between the opening and closing price.
//
// The following functions may be of use:
//
// abs - takes the absolute value of an arguement
//
// System.Double.Parse - converts a string argument into a
// numerical value.
//
// The following function will convert a comma separated string
// into an array of the column values.
//
// let splitCommas (x:string) =
// x.Split([|','|])
//---------------------------------------------------------------
let stockData =
[ "Date,Open,High,Low,Close,Volume,Adj Close";
"2012-03-30,32.40,32.41,32.04,32.26,31749400,32.26";
"2012-03-29,32.06,32.19,31.81,32.12,37038500,32.12";
"2012-03-28,32.52,32.70,32.04,32.19,41344800,32.19";
"2012-03-27,32.65,32.70,32.40,32.52,36274900,32.52";
"2012-03-26,32.19,32.61,32.15,32.59,36758300,32.59";
"2012-03-23,32.10,32.11,31.72,32.01,35912200,32.01";
"2012-03-22,31.81,32.09,31.79,32.00,31749500,32.00";
"2012-03-21,31.96,32.15,31.82,31.91,37928600,31.91";
"2012-03-20,32.10,32.15,31.74,31.99,41566800,31.99";
"2012-03-19,32.54,32.61,32.15,32.20,44789200,32.20";
"2012-03-16,32.91,32.95,32.50,32.60,65626400,32.60";
"2012-03-15,32.79,32.94,32.58,32.85,49068300,32.85";
"2012-03-14,32.53,32.88,32.49,32.77,41986900,32.77";
"2012-03-13,32.24,32.69,32.15,32.67,48951700,32.67";
"2012-03-12,31.97,32.20,31.82,32.04,34073600,32.04";
"2012-03-09,32.10,32.16,31.92,31.99,34628400,31.99";
"2012-03-08,32.04,32.21,31.90,32.01,36747400,32.01";
"2012-03-07,31.67,31.92,31.53,31.84,34340400,31.84";
"2012-03-06,31.54,31.98,31.49,31.56,51932900,31.56";
"2012-03-05,32.01,32.05,31.62,31.80,45240000,31.80";
"2012-03-02,32.31,32.44,32.00,32.08,47314200,32.08";
"2012-03-01,31.93,32.39,31.85,32.29,77344100,32.29";
"2012-02-29,31.89,32.00,31.61,31.74,59323600,31.74"; ]
let result = __
AssertEquality "2012-3-13" result
// ---------------------------------------------------------------
// Continued Learning
//
// There is MUCH more to learn about F#. You can keep going with
// the sample sections below if you finish early to continue your
// learning, but eventually, you should move on the full F# Koans
// project inside of Visual Studio. See
// https://github.com/ChrisMarinos/FSharpKoans for more details.
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// About Looping
// ---------------------------------------------------------------
// ---- looping over a list --------------------------------------
let values = [0..10]
let mutable sum = 0
for value in values do
sum <- sum + value
AssertEquality sum __
// ---------------------------------------------------------------
// ---- looping with expressions ---------------------------------
let mutable sum = 0
for i = 1 to 5 do
sum <- sum + i
AssertEquality sum __
// ---------------------------------------------------------------
// ---- looping with while ---------------------------------------
let mutable sum = 1
while sum < 10 do
sum <- sum + sum
AssertEquality sum __
(* NOTE: While these looping constructs can come in handy from
time to time, it's often better to use a more
functional approach for looping such as the functions
you learned about in the Seq module. *)
// ---------------------------------------------------------------
// ---- looping with while ---------------------------------------
type ``more about functions``() =
[<Koan>]
member this.DefiningLambdas() =
let colors = ["maize"; "blue"]
let echo =
colors
|> List.map (fun x -> x + " " + x)
AssertEquality echo __
(* The fun keyword allows you to create a function inline without giving
it a name. These functions are known as anonymous functions, lambdas,
or lambda functions. *)
// ---------------------------------------------------------------
// More About Functions
// ---------------------------------------------------------------
// ---- functions that return functions --------------------------
(* A neat functional programming trick is to create functions that
return other functions. This leads to some interesting behaviors. *)
let add x =
(fun y -> x + y)
(* F#'s lightweight syntax allows you to call both functions as if there
was only one *)
let simpleResult = add 2 4
AssertEquality simpleResult __
(* ...but you can also pass only one argument at a time to create
residual functions. This technique is known as partial appliction. *)
let addTen = add 10
let fancyResult = addTen 14
AssertEquality fancyResult __
//NOTE: Functions written in this style are said to be curried.
// ---------------------------------------------------------------
// ---- automatic currying ---------------------------------------
(* The above technique is common enough that F# actually supports this
by default. In other words, functions are automatically curried. *)
let add x y =
x + y
let addSeven = add 7
let unluckyNumber = addSeven 6
let luckyNumber = addSeven 0
AssertEquality unluckyNumber __
AssertEquality luckyNumber __
// ---------------------------------------------------------------
// ---- non curried functions ------------------------------------
(* You should stick to the auto-curried function syntax most of the
time. However, you can also write functions in an uncurried form to
make them easier to use from languages like C# where currying is not
as commonly used. *)
let add(x, y) =
x + y
(* NOTE: "add 5" will not compile now. You have to pass both arguments
at once *)
let result = add(5, 40)
AssertEquality result __
(* THINK ABOUT IT: You learned earlier that functions with multiple
return values are really just functions that return
tuples. Do functions defined in the uncurried form
really accept more than one argument at a time? *)
// ---------------------------------------------------------------
// Record Types
//
// In F#, Record Types are lightweight objects that are used to
// bundle bits of data together as properties on an object and
// give those properties meaningful names.
// ---------------------------------------------------------------
// ---- records have properties ----------------------------------
type Character = {
Name: string
Occupation: string
}
let mario = { Name = "Mario"; Occupation = "Plumber"; }
AssertEquality mario.Name __
AssertEquality mario.Occupation __
// ---------------------------------------------------------------
// ---- creating from an existing record -------------------------
let mario = { Name = "Mario"; Occupation = "Plumber"; }
let luigi = { mario with Name = "Luigi"; }
AssertEquality mario.Name __
AssertEquality mario.Occupation __
AssertEquality luigi.Name __
AssertEquality luigi.Occupation __
// ---------------------------------------------------------------
// ---- comparing records ----------------------------------------
let greenKoopa = { Name = "Koopa"; Occupation = "Soldier"; }
let bowser = { Name = "Bowser"; Occupation = "Kidnapper"; }
let redKoopa = { Name = "Koopa"; Occupation = "Soldier"; }
let koopaComparison =
if greenKoopa = redKoopa then
"all the koopas are pretty much the same"
else
"maybe one can fly"
let bowserComparison =
if bowser = greenKoopa then
"the king is a pawn"
else
"he is still kind of a koopa"
AssertEquality koopaComparison __
AssertEquality bowserComparison __
// ---------------------------------------------------------------
// ---- you can pattern match against records --------------------
let mario = { Name = "Mario"; Occupation = "Plumber"; }
let luigi = { Name = "Luigi"; Occupation = "Plumber"; }
let bowser = { Name = "Bowser"; Occupation = "Kidnapper"; }
let determineSide character =
match character with
| { Occupation = "Plumber" } -> "good guy"
| _ -> "bad guy"
AssertEquality (determineSide mario) __
AssertEquality (determineSide luigi) __
AssertEquality (determineSide bowser) __
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// Option Types
//
// In F#, option types are used to represent calculations that
// may or may not return a result.
// ---------------------------------------------------------------
// ---- option types might contain a value... --------------------
let someValue = Some 10
AssertEquality someValue.IsSome __
AssertEquality someValue.IsNone __
AssertEquality someValue.Value __
// ---------------------------------------------------------------
// ---- ...but they might not ------------------------------------
let noValue = None
AssertEquality noValue.IsSome __
AssertEquality noValue.IsNone __
AssertThrows<FILL_IN_THE_EXCEPTION> (fun () -> noValue.Value)
// ---------------------------------------------------------------
// ---- using option types with pattern matching -----------------
type Game = {
Name: string
Platform: string
Score: int option
}
let chronoTrigger = { Name = "Chrono Trigger"; Platform = "SNES"; Score = Some 5 }
let halo = { Name = "Halo"; Platform = "Xbox"; Score = None }
let translate score =
match score with
| 5 -> "Great"
| 4 -> "Good"
| 3 -> "Decent"
| 2 -> "Bad"
| 1 -> "Awful"
| _ -> "Unknown"
let getScore game =
match game.Score with
| Some score -> translate score
| None -> "Unknown"
AssertEquality (getScore chronoTrigger) __
AssertEquality (getScore halo) __
// ---------------------------------------------------------------
// ---- projecting values from option types ----------------------
let chronoTrigger = { Name = "Chrono Trigger"; Platform = "SNES"; Score = Some 5 }
let gta = { Name = "Halo"; Platform = "Xbox"; Score = None }
let decideOn game =
game.Score
|> Option.map (fun score -> if score > 3 then "play it" else "don't play")
//HINT: look at the return type of the decide on function
AssertEquality (decideOn chronoTrigger) __
AssertEquality (decideOn gta) __
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// Discriminated Unions
//
// Discriminated Unions are used to represent data types that have
// a discrete set of possible states.
// ---------------------------------------------------------------
// ---- descriminated unions capture a set of options ------------
type Condiment =
| Mustard
| Ketchup
| Relish
| Vinegar
let toColor condiment =
match condiment with
| Mustard -> "yellow"
| Ketchup -> "red"
| Relish -> "green"
| Vinegar -> "brownish?"
let choice = Mustard
AssertEquality (toColor choice) __
(* TRY IT: What happens if you remove a case from the above pattern
match? *)
// ---------------------------------------------------------------
// ---- descriminated union cases can have types -----------------
type Favorite =
| Bourbon of string
| Number of int
let saySomethingAboutYourFavorite favorite =
match favorite with
| Number 7 -> "me too!"
| Bourbon "Bookers" -> "me too!"
| Bourbon b -> "I prefer Bookers to " + b
| Number _ -> "I'm partial to 7"
let bourbonResult = saySomethingAboutYourFavorite <| Bourbon "Maker's Mark"
let numberResult = saySomethingAboutYourFavorite <| Number 7
AssertEquality bourbonResult __
AssertEquality numberResult __
// ---------------------------------------------------------------
|