something like database but not a database and sum price - haskell

How can I sum prices where month = 5 and year = 2010 ? I have date in data Subject and prices in data Sell, they are connected by id. This is my code:
-- subject id, date
data Subject = Subject Int CalendarTime deriving (Read, Show)
-- sell id, subject id, price
data Sell = Sell Int Int Double deriving (Read, Show)

Real world uses should probably use a database or at least a mapping (from the containers or unordered-containers packages), but a simple solution can be obtained using simple list comprehension.
Assuming you have simple lists of Subject and Sell:
type Subjects = [Subject]
type Sells = [Sell]
You could make an O(n*m) implementation (good for play only!):
price :: Sell -> Double
price (Sell _ _ d) = d
calTime :: Subject -> CalendarTime
calTime (Subject _ c) = c
sIdent :: Subject -> Int -- Omitted, you should use record syntax anyway
eIdent :: Sell -> Int -- Omitted
sumPred :: (CalendarTime -> Bool) -> Subjects -> Sells -> Double
sumPred js es = sum [price e | j <- js, e <- es
, sIdent j == eIdent e
, pred (calTime j)]
But as I said, that's foolish. Using a DB with Subjects keyed by CalendarTime and Sell's keyed by identity will give you a more practical solution.

Related

No instance for (Show Company) arising from a use of `print'

I have the following code
module Main where
data Company = C [Dept]
data Dept = D Name Manager [SubUnit]
data SubUnit = PU Employee | DU Dept
data Employee = E Person Salary
data Person = P Name Address
data Salary = S Float
type Manager = Employee
type Name = String
type Address = String
genCom :: Company
genCom = C [D "Research" ralf [PU joost, PU marlow], D "Strategy" blair []]
ralf, joost, marlow, blair :: Employee
ralf = E (P "Ralf" "Amsterdam") (S 8000)
joost = E (P "Joost" "Amsterdam") (S 1000)
marlow = E (P "Marlow" "Cambridge") (S 2000)
blair = E (P "Blair" "London") (S 100000)
main = print $ genCom
However I get the following error message
* No instance for (Show Company) arising from a use of `print'
* In the expression: print $ genCom
In an equation for `main': main = print $ genCom
What would be the best way to output my object?
You need to make the type an instance of the Show typeclass since print :: Show a => a -> IO (). If the type is a member of the Show typeclass, the show :: Show a => a -> String method is defined for that type. show is a bit similar to toString/ToString in Java/C#, etc.: it is a way to convert an object to a String.
You can make a custom instance, but the easiest way to do this is probably to let the compiler derive the instance automatically. You do this by adding … deriving Show to the data types you define, so:
data Company = C [Dept] deriving Show
data Dept = D Name Manager [SubUnit] deriving Show
data SubUnit = PU Employee | DU Dept deriving Show
data Employee = E Person Salary deriving Show
data Person = P Name Address deriving Show
data Salary = S Float deriving Show

How to use haskell types?

Really simple question, i can't for the life of me google the info :(
I have the following types:
type NI = Int
type Age = Int
type Balance = Int
type Person = (NI, Age, Balance)
How do I create a function that returns true if a person's age is over 65? I've tried:
retired :: Person -> Bool
retired p = p >= 65
It doesn't work obviously, i realise that even when i tried it. I'm stumped on something so simple.
as Person is a tuple you should pattern-match it like this:
retired (_,age,_) = age >= 65
and it should work
type X = Y defines a type alias. In this example, X is defined to be the same as Y. Therefore, retired :: Person -> Bool is the same as retired :: (Int, Int, Int) -> Bool.
Then we can ask the question: how do we obtain a person's age? Well, Person is the same as (Int, Int, Int), and we know the second element of the tuple is the person's age. Therefore, an equivalent question is: how do we obtain the second element of a 3-tuple?
This is done in Haskell by deconstructing the tuple, which is the opposite of constructing the tuple. An example of constructing a tuple is x = (1,2,3). To deconstruct a tuple, we use a similar notation but flip the sides: (a,b,c) = x. This is the same as (a,b,c) = (1,2,3), and the same as the three assignments a = 1; b = 2; c = 3.
Haskell allows you to use this deconstruction notation for function arguments. Therefore, retired p = e could be written retired (ni, age, balance) = e, recalling that Person is a 3-tuple. Now it is straight-forward that e should be age >= 65.
To elaborate further, retired (ni, age, balance) = e is equivalent to retired p = let (ni, age, balance) = p in e. This is useful to know because then function application is clearer. retired x is let (ni, age, balance) = x in [x/p]e where [x/p]e means "substitute x for p in e".
Another approach is to define a data type using record notation.
data Person = Person { ni :: Int, age :: Int, balance :: Int }
This defines a new type called Person, and is not the same as the 3-tuple of type (Int, Int, Int). Additionally, this defines three projection functions ni :: Person -> Int, age :: Person -> Int, and balance :: Person -> Int.
Now if we implement retired :: Person -> Bool we can do so as retired p = age p >= 65, or in point-free form retired = (>= 65) . age.
To reconnect with the first approach, can you also use deconstruction? Absolutely. A Person is constructed as x = Person m n p, and so similarly can be deconstructed as Person a b c = x. Therefore, another definition for retired is retired (Person n a b) = a >= 65.

Haskell data, custom string values

I am writing a Haskell SDK, I have everything working however I'm wanting to introduce stronger types to my search filters (url parameters).
A sample call looks like:
-- list first 3 positive comments mentioned by females
comments "tide-pods" [("limit", "3"),("sentiment", "positive"),("gender", "female")] config
While this isn't too horrible for me, I would really like to be able to pass in something like:
comments "tide-pods" [("limit", "3"),(Sentiment, Positive),(Gender, Male)] config
Or something similar.
In DataRank.hs you can see my url parameter type type QueryParameter = (String, String), as well as the code to convert the arguments for http-conduit convertParameters :: [QueryParameter] -> [(ByteString, Maybe ByteString)]
I have been experimenting with data/types, for example:
data Gender = Male | Female | Any
-- desired values of above data types
-- Male = "male"
-- Female = "female"
-- Any = "male,female"
The api also needs to remain flexible enough for any arbitrary String key, String values because I would like the SDK to keep the ability to supply new filters without depending on a SDK update. For the curious, A list of the search filters to-date are in a recently built Java SDK
I was having problems finding a good way to provide the search interface in Haskell. Thanks in advance!
The simplest way to keep it simple but unsafe is to just use a basic ADT with an Arbitrary field that takes a String key and value:
data FilterKey
= Arbitrary String String
| Sentiment Sentiment
| Gender Gender
deriving (Eq, Show)
data Sentiment
= Positive
| Negative
| Neutral
deriving (Eq, Show, Bounded, Enum)
data Gender
= Male
| Female
| Any
deriving (Eq, Show, Bounded, Enum)
Then you need a function to convert a FilterKey to your API's base (String, String) filter type
filterKeyToPair :: FilterKey -> (String, String)
filterKeyToPair (Arbitrary key val) = (key, val)
filterKeyToPair (Sentiment sentiment) = ("sentiment", showSentiment sentiment)
filterKeyToPair (Gender gender) = ("gender", showGender gender)
showSentiment :: Sentiment -> String
showSentiment s = case s of
Positive -> "positive"
Negative -> "negative"
Neutral -> "neutral"
showGender :: Gender -> String
showGender g = case g of
Male -> "male"
Female -> "female"
Any -> "male,female"
And finally you can just wrap your base API's comments function so that the filters parameter is more typesafe, and it's converted to the (String, String) form internally to send the request
comments :: String -> [FilterKey] -> Config -> Result
comments name filters conf = do
let filterPairs = map filterKeyToPair filters
commentsRaw name filterPairs conf
This will work quite well and is fairly easy to use:
comments "tide-pods" [Arbitrary "limits" "3", Sentiment Positive, Gender Female] config
But it isn't very extensible. If a user of your library wants to extend it to add a Limit Int field, they would have to write it as
data Limit = Limit Int
limitToFilterKey :: Limit -> FilterKey
limitToFilterKey (Limit l) = Arbitrary "limit" (show l)
And it would instead look like
[limitToFilterKey (Limit 3), Sentiment Positive, Gender Female]
which isn't particularly nice, especially if they're trying to add a lot of different fields and types. A complex but extensible solution would be to have a single Filter type, and actually for simplicity have it capable of representing a single filter or a list of filters (try implementing it where Filter = Filter [(String, String)], it's a bit harder to do cleanly):
import Data.Monoid hiding (Any)
-- Set up the filter part of the API
data Filter
= Filter (String, String)
| Filters [(String, String)]
deriving (Eq, Show)
instance Monoid Filter where
mempty = Filters []
(Filter f) `mappend` (Filter g) = Filters [f, g]
(Filter f) `mappend` (Filters gs) = Filters (f : gs)
(Filters fs) `mappend` (Filter g) = Filters (fs ++ [g])
(Filters fs) `mappend` (Filters gs) = Filters (fs ++ gs)
Then have a class to represent the conversion to a Filter (much like Data.Aeson.ToJSON):
class FilterKey kv where
keyToString :: kv -> String
valToString :: kv -> String
toFilter :: kv -> Filter
toFilter kv = Filter (keyToString kv, valToString kv)
The instance for Filter is quite simple
instance FilterKey Filter where
-- Unsafe because it doesn't match the Fitlers contructor
-- but I never said this was a fully fleshed out API
keyToString (Filter (k, _)) = k
valToString (Filter (_, v)) = v
toFilter = id
A quick trick you can do here to easily combine values of this type is
-- Same fixity as <>
infixr 6 &
(&) :: (FilterKey kv1, FilterKey kv2) => kv1 -> kv2 -> Filter
kv1 & kv2 = toFilter kv1 <> toFilter kv2
Then you can write instances of the FilterKey class that work with:
data Arbitrary = Arbitrary String String deriving (Eq, Show)
infixr 7 .=
(.=) :: String -> String -> Arbitrary
(.=) = Arbitrary
instance FilterKey Arbitrary where
keyToString (Arbitrary k _) = k
valToString (Arbitrary _ v) = v
data Sentiment
= Positive
| Negative
| Neutral
deriving (Eq, Show, Bounded, Enum)
instance FilterKey Sentiment where
keyToString _ = "sentiment"
valToString Positive = "positive"
valToString Negative = "negative"
valToString Neutral = "neutral"
data Gender
= Male
| Female
| Any
deriving (Eq, Show, Bounded, Enum)
instance FilterKey Gender where
keyToString _ = "gender"
valToString Male = "male"
valToString Female = "female"
valToString Any = "male,female"
Add a bit of sugar:
data Is = Is
is :: Is
is = Is
sentiment :: Is -> Sentiment -> Sentiment
sentiment _ = id
gender :: Is -> Gender -> Gender
gender _ = id
And you can write queries like
example
= comments "tide-pods" config
$ "limit" .= "3"
& sentiment is Positive
& gender is Any
This API can still be safe if you don't export the constructors to Filter and if you don't export toFilter. I left that as a method on the typeclass simply so that Filter can override it with id for efficiency. Then a user of your library simply does
data Limit
= Limit Int
deriving (Eq, Show)
instance FilterKey Limit where
keyToString _ = "limit"
valToString (Limit l) = show l
If they wanted to keep the is style they could use
limit :: Is -> Int -> Limit
limit _ = Limit
And write something like
example
= comments "foo" config
$ limit is 3
& sentiment is Positive
& gender is Female
But that is shown here simply as an example of one way you can make EDSLs in Haskell look very readable.

basic Haskell : searching through multiple lists for the same elements

I've been battling with this problem for a while. I'm trying to create an "organisation" which is a list of Gyms. These gyms are a list of People. Each person has an ID number, an age and an amount of credit.
I want the FindID function to search through the organisation to search through the list of Gyms to find the users with the inputted ID and then return the total of their credit. However, I feel I'm overcomplcating the problem and I am really struggling now.
newtype ID = ID Int deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Credit = Credit Int deriving (Show)
newtype Person = Person (ID, Age, Weight) deriving (Show)
type Gym = [Person]
type Organisation = [Gym]
getAge :: Person -> Int
getAge (Person(a,Age b,c)) = b
getID :: Person -> Int
getID (Person(ID a,b,c)) = a
getCredit :: Person -> Int
getCredit (Person(a,b,Credit c)) = c
p = Person ( ID 123, Age 65, Credit 12000)
q = Person ( ID 321, Age 64, Credit 0)
e = Person ( ID 453, Age 30, Credit 3000)
r = Person ( ID 123, Age 65, Credit 2310)
s = Person ( ID 364, Age 32, Credit 32340)
t = Person ( ID 123, Age 65, Credit 1300)
org1 = [p,q,e]
org2 = [r,s,t]
hasPerson :: Gym->Int-> Bool
hasPerson gym' id' = not (null(filter hasperson' gym') )
where
hasperson' person' = getID person' == id'
findID:: ID -> Organisation -> Int
findID id' org = total
where
IsInGym org' = hasPerson ( org' id' )
validGym = filter (IsInGym) org'
total = sum ( map getCredit validGym)
First, I would recommend using a record to represent your person, unless you have a particular reason to assign a new type to each field:
type ID = Int
type Age = Int
type Credit = Int
data Person = Person
{ personId :: ID
, personAge :: Age
, personCredit :: Credit
} deriving (Eq, Show)
type Gym = [Person]
type Organization = [Gym]
Next, you can use map to convert a Gym into [Int] with personId, then you can use the built-in elem to check if the ID given appears in that list.
hasPerson :: Gym -> ID -> Bool
hasPerson gym pId = pId `elem` map personId gym
Now, for the findID function, I would suggest renaming it to something like organizationCredit, and I would make a simpler function called gymCredit to calculate it for a single gym:
gymCredit :: ID -> Gym -> Credit
gymCredit pId gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
organizationCredit :: ID -> Organization -> Credit
organizationCredit pId org = sum $ map (gymCredit pId) org
Alternatively, you could declare your functions as
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
where pId = personId person
organizationCredit :: Person -> Organization -> Credit
organizationCredit person org = sum $ map (gymCredit person) org
EDIT: To stick with your old types, you just have to define a few extra functions yourself, then put them in your code where you need to
newtype ID = ID Int deriving (Eq, Show)
newtype Age = Age Int deriving (Eq, Show)
newtype Credit = Credit Int deriving (Eq, Show)
newtype Person = Person (ID, Age, Credit) deriving (Eq, Show)
type Gym = [Person]
type Organisation = [Gym]
personId :: Person -> ID
personId (Person (i, a, c)) = i
personAge :: Person -> Age
personAge (Person (i, a, c)) = a
personCredit :: Person -> Credit
personCredit (Person (i, a, c)) = c
idVal :: ID -> Int
idVal (ID x) = x
ageVal :: Age -> Int
ageVal (Age x) = x
creditVal :: Credit -> Int
creditVal (Credit x) = x
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = Credit $ sum $ map (creditVal . personCredit) $ filter (\p -> personId p == pId) gym
where pId = personId person
organisationCredit :: Person -> Organisation -> Credit
organisationCredit person org = Credit $ sum $ map (creditVal . gymCredit person) org
It is important to note that I've added Eq to the list of derived typeclasses for each newtype. Without it, you wouldn't be able to directly compare two IDs, you'd have to extract the values first. Another important typeclass to derive is Ord, which lets you use the <, >, <=, and >= operators, as well as a whole bunch of list functions like sort.

Basic Haskell: problems with a function

me again with another basic problem I have. I'm using ghci.
I (with help) created this working code:
newtype Name = Name String deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Weight = Weight Int deriving (Show)
newtype Person = Person (Name, Age, Weight) deriving (Show)
isAdult :: Person -> Bool
isAdult (Person(_, Age a, _)) = a > 18
However problems occur when I tried making a more complex function updateWeight that allows the user to change a Person's weight from it's previous value. Can you point out where I have gone wrong?
updateWeight :: Person -> Int -> Person
updateWeight (Person(_,_,Weight w) b = (Person(_,_,w+b))
The problem is that you can't use the _ placeholder on the right hand side of an expression. You'll have to pass through the unchanged values. Also, you must wrap the result of w + b with a Weight again. This should work:
updateWeight :: Person -> Int -> Person
updateWeight (Person(n, a, Weight w) b = (Person(n, a, Weight (w + b)))
You can get rid of the boilerplate of passing through the unchanged values by using record syntax for the Person type.

Resources