[Haskell-beginners] Re: Converting an imperative program to haskell

Daniel Fischer daniel.is.fischer at web.de
Thu Apr 29 18:16:20 EDT 2010


Am Donnerstag 29 April 2010 23:49:16 schrieb Hein Hundal:
> --- On Thu, 4/29/10, Maciej Piechotka <uzytkownik2 at gmail.com> wrote:
> > 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
> >
> > 1. Use strong typing. Or any typing
>
> I simplified the code for the post.  In the real version, I use strong
> typing.  The Card type is enumerated.  I have been using [Card] instead
> of calling it a Deck.  I could change that.
>

Whether you use [Card],

type Deck = [Card]

or

newtype Deck = D [Card]

is not important. Using enumerations for suite and value instead of Ints 
is.

> > 2. User guards instead of nested if's:
>
> In the "procloop" function, I have if's nested quite deeply.

Nested ifs (beyond, say, three levels) are a pain to decipher. Using guards 
(and maybe 'case's) will probably be a great improvement.

> I could easily use guards.  I will try that.
>
> > 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).
>
> I do have to get the information into the functions, so the only way I
> can avoid having lots of variables is by introducing new structures.
> I can do that.
>

As long as they're meaningful. Just smashing a couple of values together 
into a structure to reduce the variable count isn't good.

> > 4. Discard result you don't need:
> >
> > isDone  (LSt gs strat iA iTh i aA iB iC bDone) =
> > bDone
> > isDone  (LSt _ _ _ _ _ _ _ _ _ bDone) = bDone
>
> Yes, that's much better.

Perhaps use named field syntax,

data LoopState
    = LSt
    { gameState :: GameState
    , strat :: Strategy
    , ...
    , isDone :: Bool
    }

Then in procloop, where you just update one or two fields,

procloop lst@(LSt gs' strat iA iTh i aA iB iC bDone)
    | iA<=0 || i>=20 || actcd gs <=0 || iCd == -1
        = lst{ idx = idx lst + 1, isDone = True }
    | iCd == 1
        = lst{ idx = idx lst + 1, iThFld = iThFld lst + 1, isDone = False }
    | iThFld lst > 0
        = lst{ ... }
    | otherwise
       = lst{ ... }
      where
        iCd = stratchoose ...
        -- other let-bindings, only those needed will be evaluated

>
> proc gs' strat = let
>       iA     = 1
>       iTh    = 0
>       i      = 0
>       aA     = replicate 20 0
>       iB     = 1
>       iC     = 0
>       bDone  = False
>       gs     = apps ("<<<"++show(gs')++">>>") gs'
>       lst    = LSt gs strat iA iTh i aA iB iC bDone
>       lst2   = until isDone procloop lst
>       isDone  (LSt _  _ _ _ _ _ _  _ bDone) = bDone
>       output  (LSt gs _ _ _ _ aA iB iC _) = (iB, iC, appd aA gs)
>   in output lst2
>
> > 5. Don't reuse the same name in the same scope (like iA as
> > 1 and iA as parameter to isDone).
>
> I did hesitate to use the same parameter name twice.
>
> > 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:
>
> In the original version, I had longer variable names where they seemed
> necessary.
>
>
> The main sources of ugliness are the long lists of variables.  Every
> time I call doAct or construct a LoopState variable, I am repeating all
> those variables.  I will try changing the type of doAct to
>
> doAct :: LoopState -> LoopState

That looks promising.

>
> Cheers,
> Hein



More information about the Beginners mailing list