How do I do inheritance in haskell?

Hi! I am trying to understand how to "factor out" common functionality in haskell. (All the code is available here https://github.com/defigueiredo/inheritance-in-haskell [1]) I am receiving orderbook data from a Bitcoin Exchange, through their JSON API (available at https://www.bitstamp.net/api/order_book/ [2]) I have a generic JSON parser that parses the text response and gives me back a generic JSON object I called Jvalue. This data type represents any JSON object and is defined as follows. (see json.org for full JSON specs) ------------------ import qualified Data.Map as Map data Jvalue = Jobject (Map.Map String Jvalue) | Jarray [Jvalue] | Jstring String | Jnumber Double | Jbool Bool | Jnull deriving Show ------------------- Now, I have to convert this (if possible) into an OrderBook type. An orderbook is basically two lists. One list of the orders placed by people who want to buy, the "bids", and the other with the orders of people who want to sell, the "asks". Each order specifies what price should be paid and how many bitcoins to buy/sell (the volume). The functions for generating the lists of bids and asks are essentially the same. However, they might need to know if they are creating bids or asks. So, I may need to pass a parameter with that information. I can make that parameter a function or a type (or I may not need it). These three different ideas give rise to 3 different ways to define the orderbook: 1) We can make each Order a type with two constructors (my initial idea): ------------------- newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show) data Order = Bid {price::Price, volume::Volume} | Ask {price::Price, volume::Volume} deriving (Show) data OrderBook = OrderBook{ bids::[Order] , asks::[Order] } deriving (Show) ------------------- 2) We can make Bid and Ask types themselves ------------------- newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show) data Order = Order {price::Price, volume::Volume} deriving (Show) newtype Bid = Bid Order deriving Show newtype Ask = Ask Order deriving Show data OrderBook = OrderBook{ bids::[Bid] , asks::[Ask] } deriving (Show) ------------------- 3) We can make Order a class ------------------- newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show) class Order a where makeOrder :: String -> String -> a data Ask = Ask {aprice::Price, avolume::Volume} deriving (Show) instance Order Ask where makeOrder = makeAsk data Bid = Bid {bprice::Price, bvolume::Volume} deriving (Show) instance Order Bid where makeOrder = makeBid data OrderBook = OrderBook{ bids::[Bid] , asks::[Ask] } deriving (Show) -- makeAsk and MakeBid defined later ------------------- It is not clear to me which one of these should be used and, most importantly, which criteria should be used to choose between them. I wrote up the code to do the conversion of the Jvalue object into an OrderBook in the 3 different ways. (The third one came out of a discussion from a haskell meetup group.) (All the code is available here https://github.com/defigueiredo/inheritance-in-haskell [1]) Any insights on how to choose between them would be *greatly* appreciated! Thanks, Dimitri Links: ------ [1] https://github.com/defigueiredo/inheritance-in-haskell [2] https://www.bitstamp.net/api/order_book/

The functions for generating the lists of bids and asks are essentially the same.
Create a typeclass umbrella for them. Your option 3 looks good, but it doesn't go far enough. Whenever you're presented with an opportunity to "harden" your design by encoding problem domain invariants into the type system, you should seize it - on principled grounds. This goes back to one of the unique selling point of Haskell, which is that by careful type design you can eliminate entire classes of bugs. I tend to favor designs that look like this: newtype AskVolume = AskVolume Double deriving (Show) newtype AskPrice = AskPrice Double deriving (Show) newtype BidVolume = BidVolume Double deriving (Show) newtype BidPrice = BidPrice Double deriving (Show) data AskOrder = AskOrder { aprice :: AskPrice, avolume :: AskVolume } deriving (Show) data BidOrder = BidOrder { bprice :: BidPrice, bvolume :: BidVolume } deriving (Show) data OrderBook = OrderBook { bids :: [BidOrder], asks :: [AskOrder] } deriving (Show) This is Haskell, so there are likely opportunities to move even more stuff into the type system, but I'm not aware of them. -Dan

Hi Dan, On Thu, May 08, 2014 at 11:58:28AM +0300, Dan Serban wrote:
newtype AskVolume = AskVolume Double deriving (Show) newtype AskPrice = AskPrice Double deriving (Show)
newtype BidVolume = BidVolume Double deriving (Show) newtype BidPrice = BidPrice Double deriving (Show)
I think that's a bit over the top, because why shouldn't prices and volumes be interchangeable between Asks and Bids? Greetings, Daniel

I think that's a bit over the top, because why shouldn't prices and volumes be interchangeable between Asks and Bids?
It's a good question. I guess as long as a price / volume is always wrapped in a type that's tagged bid / ask then everything is great. You just want to avoid being the next knight capital and ending up trading the wrong side of the spread.

Because confusing them is disastrous, so there is motivation to go to
extra lengths to keep them separate. I don't have particular thoughts
on whether that motivation is sufficient in this case.
On Thu, May 8, 2014 at 7:24 AM, Daniel Trstenjak
Hi Dan,
On Thu, May 08, 2014 at 11:58:28AM +0300, Dan Serban wrote:
newtype AskVolume = AskVolume Double deriving (Show) newtype AskPrice = AskPrice Double deriving (Show)
newtype BidVolume = BidVolume Double deriving (Show) newtype BidPrice = BidPrice Double deriving (Show)
I think that's a bit over the top, because why shouldn't prices and volumes be interchangeable between Asks and Bids?
Greetings, Daniel _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Hi Dimitri,
It is not clear to me which one of these should be used and, most importantly, which criteria should be used to choose between them.
One criteria should be what you want to achieve, how you want to operate with your data.
1) newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show)
data Order = Bid {price::Price, volume::Volume} | Ask {price::Price, volume::Volume} deriving (Show)
data OrderBook = OrderBook{ bids::[Order] , asks::[Order] } deriving (Show)
This represenation of an Order gives you the ability to put Bids and Asks e.g. into the same list, but if you always want to keep them separate, then there's no point to have an ADT in the first place.
2) newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show)
data Order = Order {price::Price, volume::Volume} deriving (Show)
newtype Bid = Bid Order deriving Show newtype Ask = Ask Order deriving Show
data OrderBook = OrderBook{ bids::[Bid] , asks::[Ask] } deriving (Show)
If you want to keep Bids and Asks always separate, then this represenation seems to be more fitting.
3) newtype Volume = Volume Double deriving (Show) newtype Price = Price Double deriving (Show)
class Order a where makeOrder :: String -> String -> a
data Ask = Ask {aprice::Price, avolume::Volume} deriving (Show) instance Order Ask where makeOrder = makeAsk
data Bid = Bid {bprice::Price, bvolume::Volume} deriving (Show) instance Order Bid where makeOrder = makeBid
data OrderBook = OrderBook{ bids::[Bid] , asks::[Ask] } deriving (Show)
I might prefer the Asks and Bids data types of 2), because factoring out the common parts might have future benefits, by being able to reuse functionality on the common parts. The type class here fulfills something completely different to the previous two examples and is about the creation of your Asks and Bids. I don't think that you really need this type class, but you could just use makeAsk and makeBid without losing anything, because as long as your types in OrderBook are fixed you gain nothing. Greetings, Daniel

On Thu, May 8, 2014, at 01:26 AM, Dimitri DeFigueiredo wrote: Now, I have to convert this (if possible) into an OrderBook type. An orderbook is basically two lists. One list of the orders placed by people who want to buy, the "bids", and the other with the orders of people who want to sell, the "asks". Each order specifies what price should be paid and how many bitcoins to buy/sell (the volume). There are more representations you could choose. One example, if you want bids and asks to have the same type: data OrderType = Bid | Ask data Order = Order OrderType Price Volume And if you don't want them to have the same type: data BidType -- requires -XEmptyDataDecls data AskType data Order a = Order Price Volume type Bid = Order BidType type Ask = Order AskType -Karl
participants (6)
-
Benjamin Edwards
-
Dan Serban
-
Daniel Trstenjak
-
David Thomas
-
Dimitri DeFigueiredo
-
Karl Voelker