Retrieving number of fields in Haskell record - haskell

I am representing a table to store data as a Haskell record and I was wondering if there is a function to get the number of fields given a record?
I ask as I have a type class to represent a table and one of class functions is noOfCols; which correspond to the number of fields in a record representing a table.
data Price = Price {bid=[Float], ask=[Float]}
class Table a where
noOfCols :: a -> Int
...
instance Table Price where
noOfCols t = 2
...
So the problem is that I will be constantly adding new fields so it's possible to forget to update the instance implementation of noOfCols when I add new columns (fields) to Price; i.e. leaving it to 2 when I now have 3 or more fields.
Is there a function that can provide the number of fields for a given record so I don't have to make a manual edit everytime I change the record?

This is something that can be solved via various generic programming libraries. For example:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data Price = Price {bid :: [Float], ask :: [Float]}
deriving (Typeable, Data)
noOfCols :: Data a => a -> Int
noOfCols = gmapQl (+) 0 (const 1)
Then:
GHCi> noOfCols (Price [1,2] [3,4,5])
2
GHCi> noOfCols (0,0,0,0)
4

Your noOfCols is the arity of the constructor function Price, i.e.
noOfCols = arity Price
See Haskell: Function to determine the arity of functions? for ways to implement arity (see the accepted answer). (Note: #kosmikus is IMHO a better solution for you).
Site note: Maybe [[Float]] is a better model for you, i.e.
type Price = [[Float]]
bid :: Price -> [Float]
bid = (!! 0)
ask :: Price -> [Float]
ask = (!! 1)
noOfCols :: Price -> Int
noOfCols = length

Related

How to perform a query on a list of a custom type using map and set?

I am trying to figure out how one would be able to use map and set to generate a list based on a custom type.
For example 'type Review' which consists of: type nameOfReviewer = String, type nameOfTool = String, type numberOfStars = Int.
How would I get a list of the names of the people that have left a review so that it fits that definition.
I have tried using Set.union but haven't had any luck.
module Reviews where
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Map (Map)
import qualified Data.Map as Map
type nameOfReviewer = String
type nameOfTool = String
type numberOfStars = Int
type Review = (nameOfReviewer, nameOfTool, numberOfStars)
-- list of people that have left a review.
reviewers :: [Review] -> [nameOfReviewer]
reviewers rl = ???
To write a custom type, you need to use the data or newtype keyword. newtype is only for types with a single constructor and a single field, so we'll use data.
data Review = Review
{ nameOfReviewer :: String
, nameOfTool :: String
, numberOfStars :: Int
}
Should the number of stars really be any Int? Well, probably not, because it likely has to be between 1 and 5, or 0 and 10, or something. So that one deserves its own type.
newtype Stars = Stars Int
mkStars :: Int -> Maybe Stars
mkStars n
| 1 <= n && n <= 5
= Just (Stars n)
| otherwise
= Nothing
getStars :: Stars -> Int
getStars (Stars stars) = stars
data Review = Review
{ nameOfReviewer :: String
, nameOfTool :: String
, numberOfStars :: Stars
}
Since Stars is a full-fledged type, you can export it abstractly, forcing users to use mkStars and getStars to interact with it, thereby maintaining the star number rules.

Confusion about "type" and "data" in haskell

data MoneyAmount = Amount Float Currency
deriving (Show, Eq)
data Currency = EUR | GBP | USD | CHF
deriving (Show, Eq)
type Account = (Integer, MoneyAmount)
putAmount :: MoneyAmount -> Account -> Account
putAmount mon acc = undefined
I need to write a function that adds money to an account (display error if money added is wrong currency in account).
I know how to create an Amount
let moni = Amount 6.6 EUR
but i have no idea what to write to create an Account? (i hope that sentence makes sense) I don't know how to manipulate the input to do the whole add to account thing.
I've tried things like
let acc = Account 1 moni
My question is more how to manipulate the Account so I can write the function.
type creates a type synonym; an Account is exactly the same as an (Integer, MoneyAmount), and you write it the same way:
let acc = (1, moni)
A type is just an alias. It doesn't define a new type but instead a new name for an existing type. So you could do
type Money = Float
And you can use Money where ever you can use a Float and vice-versa. If you had
foo :: Float -> Float
foo x = 2 * x
Then
> foo (1 :: Float)
2
> foo (1 :: Money)
2
Both work fine. In your case, Account is just an alias for (Integer, MoneyAmount), so you would construct one just as you would any other tuple.
A data defines an entirely new type, and this requires new constructors. For example:
data Bool = False | True
defines the Bool type with the constructors False and True. A more complicated example would be
data Maybe a = Nothing | Just a
which defines the Maybe a polymorphic type with constructors Nothing :: Maybe a and Just :: a -> Maybe a. I've included the types of these constructors to highlight that they exist as normal values and functions. The difference between a function and a constructor is that you can do anything you want in a function, but a constructor is only allowed to take existing values and make a value of another type without performing any transformations to it. Constructors are just wrappers around values.

Deriving Enum for a sum type of records in Haskell

I have a sum type of records to represent all in-memory tables and as I will send them across the network. I have a binary protocol and need to initially pass the ordinal value (fromEnum) in the header to determine which table the data is associated with. The problem is that the sum type needs to derive from Enum but it doesn't want to.
data Table = MarketData {bid::[Float], ask::[Float]}
| Trade {price::[Float], qty::[Float]}
deriving Enum
main :: IO ()
main = do
print $ fromEnum Trade
This is the compilation error
Can't make a derived instance of `Enum Table':
`Table' must be an enumeration type
(an enumeration consists of one or more nullary, non-GADT constructors)
In the data declaration for `Table'
Any ideas of how I can do this without having to write boilerplate like this:
ordinalVal :: Table -> Int
ordinalVal tbl = case tbl of
MarketData{bid=_, ask=_} -> 0
| Trade{price=_, qty=_} -> 1
If you only want to enumerate the constructors, you can you the Data.Data module, like so:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data T a b = C1 a b | C2 deriving (Typeable, Data)
main = print $ constrIndex $ toConstr x
where
x :: T Int Int
x = C1 1 1 -- will print 1
-- x = C2 -- will print 2
If you don't want to go down the road of using the Typebale and Data type classes, you could also simply write a function Table -> Int, like you proposed.

Outputting the contents of a list of a custom data type

I have a custom data type Movie = String Int [(String,Int)] (Movie Name Year [(Fan,Rating)] and want to do a couple of things:
First I want to make a function that averages the Ints from the list of tuples and just outputs that number. So far I have this incomplete function:
avgRating :: [DataType] -> Int
avgRating [(Movie a b [(fan,rating)])] = sumRatings / (length [<mylist>])
Here I need a function sumRatings to recurse through the list and sum all the ratings, but i'm not sure where to start.
The other issue I have here is that i'm not sure what to put where <mylist> is as I would normally give the list a variable name and then use it there, but since I have split the list up to define other variables I can't name it.
I hope that makes sense, thanks.
I'm guessing you have a data structure defined as
data Movie = Movie String Int [(String, Int)]
While this works, it can be a bit cumbersome to work with when you have that many fields. Instead, you can leverage type aliases and record syntax as
type Name = String
type Year = Int
type Rating = Int
data Movie = Movie
{ mName :: Name
, mYear :: Year
, mRatings :: [(Name, Rating)]
} deriving (Eq, Show)
Now things are a bit more explicit and easier to work with. The mName, mYear, and mRatings functions will take a Movie and return the corresponding field from it. Your Movie constructor still works in the same way too, so it won't break existing code.
To calculate the average of the ratings, you really want a function that extracts all the ratings for a movie and aggregates them into a list:
ratings :: Movie -> [Rating]
ratings mov = map snd $ mRatings mov
Then you just need an average function. This will be a bit different because you can't calculate the average of Ints directly, you'll have to convert to a floating point type:
average :: [Rating] -> Float -- Double precision isn't really needed here
average rs = fromIntegral (sum rs) / fromIntegral (length rs)
The fromIntegral function converts an Int to a Float (the actual type signature is a bit more general). Since both the sum of Ints is an Int and the length of a list is always an Int, you need to convert both.
Now you can just compose these into a single function:
movieAvgRating :: Movie -> Float
movieAvgRating = average . ratings
Now, if you need to calculate the average ratings for several movies, you can apply ratings to each of them, aggregate them into a single list of ratings, then call average on that. I would suggest looking at the concatMap function. You'll be wanting to make a function like
moviesAvgRating :: [Movie] -> Float
moviesAvgRating movs = average $ ???
To answer your second question first, you can bind to a variable and unpack it simultaneously using #:
avgRating [(Movie a b mylist#[(fan, rating)])] = …
Note also that if you’re not going to be using variables that you unpack, it’s Haskell convention to bind them to _:
avgRating [(Movie _ _ mylist#[(fan, rating)])] = …
This helps readers focus on what’s actually important.
I don’t want to just give you the solution to your recursion problem, because learning to write recursive functions is an important and rewarding part of Haskell programming. (If you really want me to spoil it for you, let me know in a comment.) The basic idea, however, is that you need to think about two different cases: a base case (where the recursion stops) and a recursive case. As an example, consider the built-in sum function:
sum :: Num a => [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs
Here, the base case is when sum gets an empty list – it simply evaluates to 0. In the recursive case, we assume that sum can already produce the sum of a smaller list, and we extend it to cover a larger list.
If you’re having trouble with recursion in general, Harold Abelson and Gerald Jay Sussman present a detailed discussion on the topic in Structure and Interpretation of Computer Programs, 2nd ed., The MIT Press (Cambridge), 1996, starting on p. 21 (§§1.1.7–1.2). It’s in Scheme, not Haskell, but the languages are sufficiently similar – at least at this conceptual level – that each can serve as a decent model for the other.

Is there some method to construct value for record with lenses without underscore identifiers?

For example I have the following record
data Rec = Rec
{ _a :: Int
, _b :: Int
, _c :: Int
} deriving (Show, Eq)
makeLenses ''Rec
and I see only 2 ways to constuct new values:
Rec{_a=1,_b=2,_c=3}
Rec 1 2 3
The second variant does not look good if the number of record fields is more than a pair and underscores does not look natural in the first one.
Are there any other ways to construct record values?
If that makes sense for your type, a Default instance might be a good approach. Then you can do
def & a.~1
. b.~2
. c.~3

Resources