views:

148

answers:

2

Im a student who is really new to functional programming. Im working on a banking application where the data has been already defined as,

type Accountno = Int 
data Accounttype = Saving | Current  | FixedDeposit deriving (Show,Read)
type Accountamount = Int
type Name = String 
type Account = (Accountno, Name, Accounttype, Accountamount) 


exampleBase :: [Account]
exampleBase = [ (1,"Jennifer",Saving,1000 ) , 
    (5,"Melissa",Current,3000) ,
    (2,"Alex",Saving,1500)]

Im trying to sort the list by its account number using the following code,

sortByID :: (Ord a) => [a] -> [a]
sortByID [] = []
sortByID (l :ls) =
  let
   smallerSorted = sortByID [x | x <- ls, x <= l]
   biggerSorted = sortByID [x | x <- ls, x > l]
  in
   smallerSorted ++ [l] ++ biggerSorted


viewSortedDetails :: IO()
viewSortedDetails = 
    do
     putStrLn "Account Details Sorted By Account ID"
     let records = sortByID exampleBase
     let viewRecord = map show records
     mapM_ putStrLn viewRecord

But I do not get the expected result. as it gives me an error, informing "Instance of Ord Accounttype required for definition of viewSortedDetails".Please can some one help me to overcome this problem Thanks a lot!

+5  A: 

Well, the problem is that you're using ordering comparisons, such as <=, on two Account values, which would require Account to be an instance of Ord. Now, Account is a synonym for a four-element tuple, which are defined to be instances of Ord when all the types in the tuple are. Accountno, Name, and Accountamount are all synonyms for types with Ord instances, but Accounttype is not.

You could make it possible to sort Account values directly by making Accounttype an instance of Ord, which you can do by simply adding it to the deriving clause.

However, if you want to specifically sort only by the account number, not the other elements of the tuple, you'll need to do something differently. One option would be to make Account a data type with a custom Ord instance:

data Account = Account Accountno Name Accounttype Accountamount deriving (Eq, Show, Read)

instance Ord Account where
    (...)

Then you can define the ordering however you like.

Alternatively, you can leave it as is and instead only compare the element you want instead of the entire Account value, using something like this:

accountNo :: Account -> Accountno
accountNo (n,_,_,_) = n

...and then doing the comparison with something like smallerSorted = sortByID [x | x <- ls, accountNo x <= accountNo l]. The standard libraries also include a function on for this purpose, but it would awkward to use in this case.


A few other remarks, which are less relevant to your question, on the general subject of Haskell code:

  • Defining Account as a data type, probably using the record syntax, would be nicer than using a type synonym here. Large tuples can be awkward to work with.

  • Accountno and Accountamount should probably be different types as well, to avoid mixing them with other Ints: the first because doing arithmetic on account numbers makes little sense, the latter in part because (I'm guessing) you're implicitly using fixed point arithmetic, such that 100 actually means 1.00, and in general just to avoid confusion.

  • In fact, Int is probably a bad choice for Accountamount anyway: Why not something from Data.Fixed, Ratio Integer, or a base-10-safe floating point type (although there isn't one in the standard libraries, unfortunately).

  • The standard libraries of course include sorting functions already--I'm assuming the reimplementation is for learning purposes, but in practice it could all be replaced by something like sortBy (compare `on` accountNo).

camccann
Thanks a lot for ur answer. A lot appreciated. I tried out the second method u have mentioned. But still it gives me an error informing, Inferred type is not general enough*** Expression : sortByID*** Expected type : Ord a => [a] -> [a]*** Inferred type : Ord (Int,[Char],Accounttype,Int) => [(Int,[Char],Accounttype,Int)] -> [(Int,[Char],Accounttype,Int)] Can you be kind enough to help me with this. Once again thanks a lot.
@user421607: Extracting an element from the tuple puts more restrictions on the type being sorted: It requires a list of `Account`, not a generic list like `[a]`. If you just remove the type signature it should infer the correct one, but `[Account] -> [Account]` should be what it wants.
camccann
I tired out in that manner too...But still it gave me errors.I'l try out ur first method. Thanks a lot for the help.
@user421607: Adding the `accountNo` function and removing the type signature on `sortByID` worked for me. Did you forget to add the `accountNo` function in one of the comparisons?
camccann
Thanks a lot.It worked for me too :) . This was a huge help for the time being.
There is a fixed point float: in module Data.Fixed: data Fixed a, where "Fixed E2" is the fixed point Integer with two decimal digits after the dot. <http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Fixed.html>
comonad
@comonad: That's still fixed-precision, which wasn't quite what I had in mind--but I had forgotten about it all the same, and it'd make more sense than using `Ratio`, so thanks!
camccann
Er, awkward? <code>sortByID = sortBy (compare \`on` accountNo)</code>. Reads like English.
luqui
@luqui: Awkward to stick into the list comprehensions in his code as it stands. Not awkward at all when used your way, since that's exactly what it's meant for! I was trying not to suggest totally rearranging his code, since I don't know the point of the learning exercise. I did mention the definition using `sortBy` in my remarks at the end.
camccann
@luqui: Of course, the *really* awkward part is trying to use backtick infix notation in inline code snippets on Stack Overflow. Sigh.
camccann
A: 

is it possible to post the entire code for sorting pls.....it isnt working for me!

abcd
could u pls post the code for sorting here....to clarify my doubts. thank u
abcd