Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Beginning F#: Card Tricks : Page 4

Discover the power of F# by writing a complete card-shuffling program in only 92 lines of code.


advertisement
Pattern Matching and Functional Polymorphism
One of F#'s notable features is its ability to make inferences about expressions. Here's an F# Interactive exploration showing how F# makes inferences about types:

> let list1 = [ 1; 2; 3];; val list1 : int list > let list2 = [ [ "apple"; "orange" ]; [ "pear"; "mango" ] ];; val list2 : string list list > let add x y = x + y ;; val add : int -> int -> int > add 2 3;; val it : int = 5 > add "two" "three";; add "two" "three";; ----^^^^^^ stdin(8,5): error FS0001: This expression has type string but is here used with type int.

F# Interactive's response to the first line of code above is to examine the list1 expression and determine that it is of type int list: a list of integers. F# similarly infers that the list2 expression is of type string list list: a list of lists of strings.

In the third entry, F# examines the add function, and by looking at the use of the x and y arguments, infers that it is of type int -> int -> int: a function that accepts two integers and evaluates to an integer.

You can use F#'s inference capabilities to look for specific patterns in types and expressions and then take action accordingly. This process is called pattern matching, and you've already see it in action throughout this article. First, you used it to extract the elements of a 3-tuple:

> let tupleOfThree = ("Abraham", "Lincoln", 16);; val tupleOfThree : string * string * int > let x, y, z = tupleOfThree;; val z : int val y : string val x : string > x;; val it : string = "Abraham" > y;; val it : string = "Lincoln" > z;; val it : int = 16

You also used it to extract only the relevant elements of 2-tuple in a comparison function:



let sortedWeightedCards = List.sort (fun (_, leftWeight) (_, rightWeight) -> leftWeight-rightWeight) weightedCards

The match keyword is another way to take advantage of pattern matching:

> let findTwosInTuple tuple = match tuple with | (2, _) -> "2 is the first member of the tuple!" | (_, 2) -> "2 is the second member of the tuple!" | _ -> "There isn't a 2 in this tuple";; val findTwosInTuple : int * int -> string > findTwosInTuple (2, 0);; val it : string = "2 is the first member of the tuple!" > findTwosInTuple (3, 0);; val it : string = "There isn't a 2 in this tuple" > findTwosInTuple(99, 2);; val it : string = "2 is the second member of the tuple!"

Pattern matching is useful in far more ways than this article can list, but the most prominent use for the construct in F# is functional polymorphism: allowing a function to change its behavior based on the type (or pattern) of the expression it's acting on. The playing card application provides a perfect example.

After the playing card application draws cards from the deck, you need to display the results in the console window. Here's a function called printCard that accepts a playing card, but that behaves differently based on the card type (face card, rank card, or joker). The match construct makes this trivial:

let printCard card = match card with | RankCard(rank, suit) -> printfn "%A of %A" rank suit | FaceCard(Ace, suit) -> printfn "Bam! An Ace of %A" suit | FaceCard(face, suit) -> printfn "Nice! %A of %A" face suit | Joker -> printfn "Joker is wild!"

The printCard function is polymorphic with regard to rank cards, aces, face cards and jokers. The polymorphic behavior in this case is a trivial printfn expression, but you could easily replace that with function expressions that encapsulate complex behaviors.

The application's main function is now complete:

let main = let shuffledCards = shuffle fiftyFourCards let (drawnCards, remainingDeck) = draw 10 shuffledCards let printCard card = match card with | RankCard(rank, suit) -> printfn "%A of %A" rank suit | FaceCard(Ace, suit) -> printfn "Bam! An Ace of %A" suit | FaceCard(face, suit) -> printfn "Nice! %A of %A" face suit | Joker -> printfn "Joker is wild!" List.iter printCard drawnCards

Here's the console output from one execution:

Bam! An Ace of Clubs Nice! Queen of Spades Joker is wild! Six of Spades Bam! An Ace of Spades Eight of Diamonds Nine of Clubs Five of Clubs Nice! King of Diamonds Three of Clubs

As you can see by the terseness of the code you've written, F# is an elegant, logical language. You've explored the functional programming approach and some of F#'s native features, such as:

  • Discriminated unions
  • Tuples
  • Lists and list sequence expressions
  • Functions and recursion
  • Pattern matching and functional polymorphism
You've created a simple yet powerful playing card application using only 92 lines of code. Stay tuned for a follow-up article that explores some of F#'s more advanced features to implement the core of any playing card game: the rules.



Seth Livingston is the CTO of Adventos, LLC, a training, mentoring, and delivery partner for Microsoft solutions and software development best practices. Seth splits his time among seminars, classes, mentoring, and guiding Adventos' technical direction. He lives in McKinney, Texas.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap