On Tue, Jul 26, 2011 at 11:58 PM, Tim Cowlishaw <tim@timcowlishaw.co.uk> wrote:
On Tue, Jul 26, 2011 at 11:14 PM, Alexander Solla <alex.solla@gmail.com> wrote:

> data OrderType = Market Size | Limit LimitPrice Expiration Size | Stop
> (Either Percent Price)
> newtype Sell = Sell OrderType
> newtype Buy = Buy OrderType
> newtype Order = Order (Either Buy Sell)

> size :: Order -> Int
> size (Order (Left (Buy (Market s))) = s
> size (Order (Left (Buy (Limit _ _ s))) = s
> etc.

Aah, thank you - this is really neat. So now, I can write (for
instance) an Eq instance for OrderType and use deriving (Eq) on the
newtypes that wrap it, and my Order can be a concrete type, but still
encapsulates all the different types of order.

Thank you!

No problem.  This is more-or-less how type classes work internally, with fewer restrictions (but some more implicit passing around of stuff).  Notice that my Order type "corresponds" with your Order typeclass.  My OrderType type value constructors correspond to all your Order types.  In other words, a typeclass is a fancy "open" "union" type.  I never use type classes unless I need that openness property.

The problem with this approach is that it can become verbose very quickly.  It can be mitigated some by defining accessors for the newtypes, and using function composition.

So instead of:
> newtype Sell = Sell OrderType
> newtype Buy = Buy OrderType
> newtype Order = Order (Either Buy Sell)

I would personally use
> newtype Sell = Sell { unSell :: OrderType }
> newtype Buy = Buy { unBuy :: OrderType }
> newtype Order = Order { unOrder :: Either Buy Sell }

where "un" should be read like "unwrap".  These unwrappers can help cut down on the size of pattern matches.  I'll give an example shortly.

I suggested using Maybe to deal with nonsense semantics/undefinedness.  All orders have a size/quantity, but not all have a limit price.  So we might write an accessor like:

limitPrice' :: OrdeType -> Maybe Price
limitPrice'  (Limit l _ _) = Just l
limitPrice' _ = Nothing

We have turned a "partial" function into a "total" function by embedding it in (Order -> Maybe Price).  This cuts down on bugs.

Now that easy accessor for all orders:

limitPrice :: Order -> Maybe Price
limitPrice = limitPrice' . either (unBuy) (unSell) . unOrder
                 
We might even want to stick limitPrice' in a where clause for limitPrice, depending on whether you expect reuse or not.