understanding cyclic/recursive data types in haskell - haskell

I'm trying to fully understand haskell's data types, so I created these:
data District = District {nameOfCity :: String,
subDistricts ::[DistrictUnit]}
deriving (Show, Eq)
data DistrictUnit = DistrictUnit District
| BranchUnit Branch
deriving (Show, Eq)
data Branch = Branch {nameOfBranch :: String,
numOfEmployees :: Int,
sales :: Int}
deriving (Show, Eq)
Here are some examples:
a = District {nameOfCity = "Berlin", subDistrcits = []}
b = District {nameOfCity = "New York",
subDistricts =
[DistrictUnit
(District {nameOfCity = "Amsterdam",
subDistricts =
[DistrictUnit
(District {nameOfCity = "London",
subDistricts = []})]})]}
c = District {nameOfCity = "Iowa" ,
subDistricts =
[BranchUnit
(Branch {nameOfBranch = "Omaha",
numOfEmployees = 3,
sales = 2343})]}
Now I'm trying to build two functions: getNameOfCity and getNameOfBranch:
getNameOfCity b -> ["New York", "Amsterdam", "London"]
But I have massive pattern matching issues. I just don't know how to build a function that works for all inputs.
Here are my attempts:
getNameOfCity :: District -> [String]
getNameOfCity (District a b) = [a]
This function will only give me the first name:
["New York"]
So I tried this:
getNameOfCity (District a [DistrictUnit (District b c)]) = [a, b]
And (of course) that one will only give me the first two names:
["New York", "Amsterdam"]
How can I make that getNameOfCity work for all inputs? And will getNamesOfBranch work the same way (since it has different parameters)? Thank you in advance :)

Rather than trying to do everything with a single function, it's easier to write separate helpers:
allDistrictNames :: District -> [String] -- This was your `getNameOfCity`
allDistrictNames_u :: DistrictUnit -> [String]
allDistrictNames_ul :: [DistrictUnit] -> [String]
allDistrictNames and allDistrictNames_u can now be implemented with simple pattern matches and calls to each other. allDistrictNames_ul needs to call allDistrictNames_u on all list elements, and combine the results. There's a standard function for this exact purpose.

You want getNameOfCity applied to a District to return its nameOfCity field, prepended to the list of city names from each of its subDistricts that are Districts and not Branches.
So you had a good starting point with something like this:
getNameOfCity :: District -> [String]
getNameOfCity (District name units) = [name]
But of course you need to do something with units. The pattern you wrote in your second example, [DistrictUnit (District b c)], only matches when the subDistricts field contains a list of exactly one element, where that element is also a DistrictUnit, and extracts only the nameOfCity field (b) from that district, ignoring any further nested subdistricts (in c).
To help you with this, I’m going to give you a template for you to fill in, following a common pattern that’s very helpful when you’re getting familiar with Haskell: working back from the desired result, breaking down the problem into small steps, and giving each step a name and a type signature. That way, if you make a mistake, the compiler will produce much more comprehensible error messages. Then, once you have a working solution, you can remove the intermediate variables and consolidate the steps into a more compact expression.
First, your desired result is the root district name prepended to the list of names of all subdistricts.
getNameOfCity :: District -> [String]
getNameOfCity (District name units) = let
allCities :: [String]
allCities = …
in name : allCities
To construct this, you’ll need to iterate over the units and extract the names from each district, then concatenate all the results.
getNameOfCity :: District -> [String]
getNameOfCity (District name units) = let
districtCities :: [[String]]
districtCities = … -- List of list of city names for each ‘District’
allCities :: [String]
allCities = … -- Concatenated ‘districtCities’
in name : allCities
The iteration over the units can be done in a few ways, but what I would do is extract only the Districts first so you have a narrower, more precise type to work with, and then you can simply apply getNameOfCity recursively on them.
getNameOfCity :: District -> [String]
getNameOfCity (District name units) = let
-- Extract only those ‘units’ that are ‘District’s
districts :: [District]
districts = …
-- Hint:
-- Hoogle ‘(a -> Maybe b) -> [a] -> [b]’
-- and define ‘getDistrict :: DistrictUnit -> Maybe District’;
-- or use a list comprehension of the form: ‘[… | … <- units]’
districtCities :: [[String]]
districtCities = …
allCities :: [String]
allCities = …
in name : allCities
You could follow a very similar approach for the branch names; it’s a good exercise to consider how you might abstract over the commonalities, once you have both of them in front of you.
It’s also a common higher-level approach to solve this sort of problem using the Semigroup & Monoid typeclasses, which are mathy names for the sets of types that can be “appended” associatively in some way (the <> operator), and have a default “empty” value (mempty) which is a no-op for appending. The general method is to map each value to a “summary” value—here, the list of city names—and then fold those summaries together, conveniently combined into a single operation called foldMap. For instance:
districtCities :: District -> [String]
districtCities (District city units) = [city] <> foldMap unitCities units
unitCities :: DistrictUnit -> [String]
-- Recursively summarise district.
unitCities (DistrictUnit district) = districtCities district
-- Return “empty” value for branch.
unitCities (BranchUnit branch) = mempty
This generalises to accumulating other fields, like the branch names:
districtBranches :: District -> [String]
districtBranches (District _ units) = foldMap unitBranches units
unitBranches :: DistrictUnit -> [String]
unitBranches (DistrictUnit district) = districtBranches district
unitBranches (BranchUnit branch) = [nameOfBranch branch]
Or other types of values, like the total sales, using the Sum monoid:
import Data.Monoid (Sum(..))
totalSales :: District -> Int
totalSales district = getSum (districtSales district)
-- Or: totalSales = getSum . districtSales
districtSales :: District -> Sum Int
districtSales (District _ units) = foldMap unitSales units
unitSales :: DistrictUnit -> Sum Int
unitSales (DistrictUnit district) = foldMap districtSales district
unitSales (BranchUnit branch) = Sum (branchSales branch)
Or even multiple values at once, using e.g.: (Sum 1, ["London"]) <> (Sum 2, ["Paris"]) = (Sum 3, ["London", "Paris"]). Notice how all these implementations have similar structure, too, though, and consider how you might abstract over this “folding” pattern generically.

Related

how to filter sum types in Haskell

for example
data CampingStuff = Apple String Int
| Banana String Int
| Pineapple String Int
| Table String Int
| Chairs String Int
I want to have a query function
pickStuff :: [CampingStuff] -> ??? -> [CampingStuff]
the ??? I want to pass Apple then the pickStuff is going to filter out all stuffs like
Apple "Jane" 3
Apple "Jack" 5
Apple "Lucy" 6
something I can think of is like
pickStuffs stuffs dummyStuff
= filter
(\x ->
(x == dummyStuff)
stuffs
pickStuffs stuffs (Apple "" 0)
instance Eq CampingStuff where
compare (Apple name1 number1) (Apple name2 number2)
= True
the drawback of it is :
passing extra parameters to dummy value is not elegant and is not making any sense "" 0
it has to implement all the value constructor in Eq type class ( Apple, Table , Chair)
it is not scalable as in the future I would like to filter out all the apples from Janes
like this (Apple "Jane" _)
Thank you for reading this and appreciate any help how to filter on this [CampingStuff] by Data Constructor like Apple/Table ?
The problem is that unsaturated constructors can't really be compared by value. The only things you can do with them are invoke them or pattern match on them. So if you want a function that tests for Apple, it'll have to be totally different from a function that tests for Banana - they can't share any code, because they have to compare against a different set of patterns.
This is all much easier if you refactor your type to remove the obvious duplication, leaving you with saturated value constructors. The generated Eq instance is all you'll need for comparing types:
data StuffType = Apple | Banana | Pineapple | Table | Chairs deriving Eq
data CampingStuff = Stuff { stuffType :: StuffType
, owner :: String
, quantity :: Int
}
Then you can easily write a function of type CampingStuff -> Bool by composing a couple functions.
hasType :: StuffType -> CampingStuff -> Bool
hasType t s = stuffType s == t
and use that to filter a list:
pickStuff :: StuffType -> [CampingStuff] -> [CampingStuff]
pickStuff = filter . hasType
In the comments, you ask: What if my constructors weren't all uniform, so I couldn't extract everything out to a product type with an enum in it?
I argue that, in such a case, you won't be happy with the result of a pickStuff function no matter how it's implemented. Let's imagine a simpler type:
data Color = Red | Green
data Light = Off | On Color
Now, you might wish to filter a [Light] such that it includes only lights that are On, regardless of their color. Fine, we can implement that. We won't even worry about generalizing, because the type is so small:
ons :: [Light] -> [Light]
ons = filter on
where on Off = False
on (On _) = True
Now you have lights :: [Light], and you can get onLights = ons lights :: [Light]. Amazing. What will you do with onLights next? Perhaps you want to count how many of each color there are:
import qualified Data.Map as M
colorCounts :: [Light] -> M.Map Color Int
colorCounts = M.fromListWith (+) . map getColor
where getColor (On c) = (c, 1)
colorCounts has a problem: it assumes all the lights are On, but there's no guarantee of that in the type system. So you can accidentally call colorCounts ls instead of colorCounts (ons ls), and it will compile, but give you an error at runtime.
Better would be to just do your pattern matching at the point when you'll know what to do with the results. Here, that's inside of colorCounts: just add a case for Off, and use mapMaybe instead of map so you have a chance to throw away values you don't like:
colorCounts' :: [Light] -> M.Map Color Int
colorCounts' = M.fromListWith (+) . mapMabye getColor
where getColor (On c) = Just (c, 1)
getColor Off = Nothing
The same arguments all hold for more complicated types: don't pattern match on a value until you're ready to handle all the information you might find.
Of course, one way to handle such information is to put it into a new type that contains only the information you want. So you could very well write a function
colorsOfOnLights :: [Light] -> [Color]
colorsOfOnLights = mapMaybe go
where go Off = Nothing
go (On c) = Just c
This way, you can't possibly mix up the input of the "filter" function with the output: the output is clearly divorced from the original Light type, and its values can only have come from the On lights. You can do the same thing for your CampingStuff type by extracting a new product type for each of the constructors:
data CampingStuff = Apple AppleData
| Banana BananaData
-- ...
data AppleData = AppleData String Int Bool
data BananaData = BananaData String Int
-- ...
asApple :: CampingStuff -> Maybe AppleData
asApple (Apple ad) = Just ad
asApple _ = Nothing
apples :: [CampingStuff] -> [AppleData]
apples = mapMaybe asApple
You'll need separate functions for asApple and for asBanana and so on. This seems cumbersome, and I don't exactly disagree, but in practice people don't really need large numbers of functions like this. It's usually better to do as I described before: delay the pattern match until you know what to do with the results.
For the function you want to have, you could create functions such as
isApple :: CampingStuff -> Bool
isApple Apple{} = True
isApple _ = False
and then use filter isApple. When you want to filter by Jane, you add another 5 functions for each type, like isAppleFrom :: String -> CampingStuff -> Bool and do filter (isAppleFrom "Jane").
Another approach is the following:
data StuffType = AppleT | BananaT | PineappleT | TableT | ChairsT deriving Eq
data Query = ByStuff StuffType | ByName String deriving Eq
pickStuff :: [CampingStuff] -> [Query] -> [CampingStuff]
pickStuff xs qs = filter cond xs
where
cond :: CampingStuff -> Bool
cond x = all (\q -> case (q, x) of
(ByStuff AppleT, Apple{}) -> True
...other pairs...
(ByName name1, Apple name2 _) -> name1 == name2
...
_ -> False) qs
That is, separate querying from the data types. The above is an example and may be written better.

Manipulating a value within several layers of monads

My goal is to pull out numVal4 from Employment -- but doing it from the level of the MyEmployment value.
data Employment = Employment
{ textVal1 :: Text
, textVal2 :: Text
, textVal3 :: Text
, numVal4 :: Money }
data MyEmployment = MyEmployment Person Employment
MyEmployment (Person "Me") (Employment "This" "is a" "test" 55)
fmap . fmap (fromInteger (Employment _ _ _ x)) MyEmployment
EDIT:
I should have bee more specific. I need to access the value from within a DMap. The actual code looks like something closer to this:
thing = dmap ! (Some_Function . Some_OtherFunction $ MyEmployment)
And I need to get the numeric value from inside of Employment, with some kind of wrapper like:
thing = fmap (fromIntegral (Employment _ _ _ x)) (dmap ! (Some_Function . Some_OtherFunction $ MyEmployment)
Apart from all the syntax issues, I think what you are trying to do is the following.
This is how you declare a data type:
data Employment = Employment
{ textVal1 :: Text
, textVal2 :: Text
, textVal3 :: Text
, numVal4 :: Money }
Note the ::s which are not =, and note the indentation.
MyEmployment seems to be a data type and by MyEmployment :: Person Employment I think you in fact mean
data MyEmployment = MyEmployment Person Employment
In this setting then, If you want to take a value of type MyEmployment, and apply a function to the numVal4 field of the Employment field, record syntax allows you to write:
foo :: (Money -> Money) -> MyEmployment -> MyEmployment
foo f (MyEmployment p e#(Employment {numVal4 = nv}))
= MyEmployment p ( e {numVal4 = f nv})

Defining a High Order function that acesses data records

I pretend to create a high order function that uses as one of its parameters a function which belongs to the record of a certain Data Type.
For Example:
type Debt = Float
type Power = Int
Data State = S String Debt
Data City = C String Power Debt
Data Country = Country
{ states :: [State]
, cities :: [City] }
upDetail :: (Country -> [a])
-> ([a] -> b -> [a])
-> b -> Country -> Country
upDetail f g b country = country { f = new }
where old = f country
new = g old b
What the function above is supposed to do is pick an element of the record of the Country (with the function type Country -> [a]) and alter it according to a certain function type [a] -> b -> [a] and a certain b
However, when i try to compile this i get an error saying :
‘f’ is not a (visible) constructor field name
Is there any way i can overcome this problem? I thought of using Maybe Country as my result but i don't know how to do this.
As the comments mention, the normal solution to this is to use lenses:
upDetail :: Lens Country [a]
-> ([a] -> b -> [a])
-> b -> Country -> Country
upDetail f g b country = set f country new
where old = get f country
new = g old b
However, lenses aren't that hard to get a handle on, especially for so simple a purpose.
The simplest way of expressing a lens is as a getter and a setter function:
data Lens s a = Lens
{ get :: s -> a
, set :: s -> a -> s
}
_states :: Lens Country [State]
_states = Lens states $ \c new -> c { states = new }
_cities :: Lens Country [City]
_cities = Lens cities $ \c new -> c { cities = new }
This lets us modify the cities or states of a country pretty easily:
λ Country [] []
Country {states = [], cities = []}
λ upDetail _cities (\cs c -> c:cs) (C "Hereford" 100 3000) it
Country {states = [], cities = [C "Hereford" 100 3000.0]}
λ upDetail _states (\cs c -> c:cs) (S "Delmarva" 4) it
Country {states = [S "Delmarva" 4.0], cities = [C "Hereford" 100 3000.0]}
Lens get slightly more complex once you start thinking about composing lenses, which you're not getting into here, but you could.

basic Haskell : list comprehension

Me again, I'm trying to iterate through my list of customers to find the correct customer and when I find them I want to display any non-zero int's that are attached to them. I'm not sure how to proceed. I know there will only be 1 record of the person's name in the shop.
type Name = String
type Customer = (Name,Int,Int)
type Shop = [Customer]
shop = [cust1, cust2]
cust1 = ("Steve", 321, 123) :: Customer
cust2 = ("John", 0,678) :: Customer
getName :: Customer -> Name
getName (a, b,c) = a
getNumbers :: Customer -> [Int]
getNumbers (a,b,c) = filter (/=0) [b,c]
rental:: Shop-> Name -> [Int]
rental shop' name' = map getNumbers [ x|x<-shop',getName x == name']
It is very useful to read error message!
test23.hs:10:9:
Couldn't match type `(Name, t0)' with `(Name, Int, Int)'
Expected type: Customer
Actual type: (Name, t0)
You have
getName (a, b) = a
but is defined
type Customer = (Name,Int,Int)
The right function looks like
getName (a, _, _) = a
After correct, you could see next meassage:
test23.hs:17:26:
Couldn't match type `[Int]' with `Int'
Expected type: Customer -> Int
Actual type: Customer -> [Int]
In the first argument of `map', namely `getNumbers'
...
In an equation for `rental'
But error is not in getNumbers, but in signature of rental:: Shop-> Name -> [Int]. Must be:
rental:: Shop-> Name -> [[Int]]
Your answer is pretty close. First of all, you need to update getName to take a 3-tuple, and second you should use concatMap getNumbers instead of map getNumbers.
Although, it looks like you're going to be adding new fields to your Customer type, so I would recommend that you switch to using a record instead:
data Customer = Customer
{ custName :: Name
, custVal1 :: Int -- I don't know what these are, so use real names
, custVal2 :: Int
} deriving (Eq, Show)
And now you could get rid of getName and do
getNumbers :: Customer -> [Int]
getNumbers c = filter (/= 0) [custVal1 c, custVal2 c]
rental :: Shop -> Name -> [Int]
rental shop' name' = concatMap getNumbers [x | x <- shop', custName x == name']
Now if you were to add another field to Customer, you don't have to update all your functions that don't depend on that field.

Confused about custom data types in Haskell

The task: I am trying to create a custom data type and have it able to print to the console. I also want to be able to sort it using Haskell's natural ordering.
The issue: Write now, I can't get this code to compile. It throws the following error: No instance for (Show Person) arising from a use of 'print'.
What I have so far:
-- Omitted working selection-sort function
selection_sort_ord :: (Ord a) => [a] -> [a]
selection_sort_ord xs = selection_sort (<) xs
data Person = Person {
first_name :: String,
last_name :: String,
age :: Int }
main :: IO ()
main = print $ print_person (Person "Paul" "Bouchon" 21)
You need a Show instance to convert the type to a printable representation (a String). The easiest way to obtain one is to add
deriving Show
to the type definition.
data Person = Person {
first_name :: String,
last_name :: String,
age :: Int }
deriving (Eq, Ord, Show)
to get the most often needed instances.
If you want a different Ord instance, as suggested in the comments, instead of deriving that (keep deriving Eq and Show unless you want different behaviour for those), provide an instance like
instance Ord Person where
compare p1 p2 = case compare (age p1) (age p2) of
EQ -> case compare (last_name p1) (last_name p2) of
EQ -> compare (first_name p1) (first_name p2)
other -> other
unequal -> unequal
or use pattern matching in the definition of compare if you prefer,
compare (Person first1 last1 age1) (Person first2 last2 age2) =
case compare age1 age2 of
EQ -> case compare last1 last2 of
EQ -> compare first1 first2
other -> other
unequal -> unequal
That compares according to age first, then last name, and finally, if needed, first name.

Resources