
On Thu, 2010-04-29 at 13:29 -0700, Hein Hundal wrote:
I figured I should try a larger program in Haskell, so I am converting one of my Mathematica programs, a simulator for the card game Dominion, over to Haskell. Most of that is going well except for one patch of imperative code. My Haskell version of this code is ugly. I was hoping someone could recommend a better way to do it. I will paste a simplified version of the code below. If necessary, I can provide all the other code used for this simplified example (about 30 more lines of Haskell code, or an equivalent program in C++ (130 lines of code).
1. Use strong typing. Or any typing data Card = Value `Of` Color data Color = Spades | Hearts | Diamonds | Clubs data Value = A | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | J | Q | K type Deck = [Card] Now saying K `Of` Spades is nicer then 32 or anything. Also It allows to have it much nicer: check (A `Of` Clubs) = "You guessed my card!" check _ = "Try again!" You won't get invalid card (as 53) - it is guaranteed by the system. 2. User guards instead of nested if's: test a b c | a == b = "Hello" | a == c = "Hi" | b == c' = "Howdy" | otherwise = "Goodbye" where c' = c + 1 vs. test a b c = if a == b then "Hello" else if a == c then "Hi" else if b == c' then "Howdy" else "Goodbye" where c' = c + 1 3. Don't use too much variables. 6-8 is probably the maximum you should deal with (human short-term memory holds about 5-10 points of entry. Split functions into smaller functions (even in where). 4. Discard result you don't need: isDone (LSt gs strat iA iTh i aA iB iC bDone) = bDone isDone (LSt _ _ _ _ _ _ _ _ _ bDone) = bDone 5. Don't reuse the same name in the same scope (like iA as 1 and iA as parameter to isDone). 6. While Haskell have long tradition of having short namesit is not always good (see 3). Use them only if you are sure it is clear what they mean: map f (x:xs) = f x:map f xs -- Good - take something x from list of somethins map _ [] = [] Regards PS. Sorry - I haven't had time to look into code very deeply.