[Haskell-cafe] Idiomatic ways to make all instances of a certain class also instances of another?

Alexander Solla alex.solla at gmail.com
Wed Jul 27 21:41:57 CEST 2011


On Tue, Jul 26, 2011 at 11:58 PM, Tim Cowlishaw <tim at timcowlishaw.co.uk>wrote:

> On Tue, Jul 26, 2011 at 11:14 PM, Alexander Solla <alex.solla at 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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20110727/62303702/attachment.htm>


More information about the Haskell-Cafe mailing list