Headline

Using functorial map and fold to access company data

Characteristics

Company data is quite hetereogenous. It's a container of kinds. There is a company (at the top); there is departments and employees in the tree-like structure of a company; there is also names (of employees, departments, and companies); further, there are addresses (of employess), and there are salaries of employees including managers as a special kind of employees.

We would like to access company structures in a functorial style. We have in mind the common operations Feature:Total and Feature:Cut. For these operations to fit into the functorial framework, we need to parametrize the company types appropriately in terms of salary-type positions.

Illustration

We need companies and descendants to be parametrized in salaries:

data Company s = Company Name [Department s]
...

We perform salary cut by functioral map:

cut :: Company Float -> Company Float
cut = fmap (/2) 

To this end, we assume the Company type to be parametrized in salary positions.

Likewise, we total salaries by an application of the foldr function:

total :: Company Float -> Float
total = foldr (+) 0

Here, we leverage the fact that we can access all type-parameter positions in a monoidal manner and hence essentially reduce all salaries.

Architecture

Company data model parameterized in salary position

We set up compamy data in a parameterized manner as follows:

-- The data model is parameterized in what's going to be Float-based salaries
data Company s = Company Name [Department s]
 deriving (Eq, Read, Show)
data Department s = Department Name (Manager s) [Department s] [Employee s]
 deriving (Eq, Read, Show)
data Employee s = Employee Name Address s
 deriving (Eq, Read, Show)
type Manager s = Employee s
type Name = String
type Address = String

Companies as functors over salaries

Here are the Functor instances:

instance Functor Company
  where
    fmap f (Company n ds) = Company n ds'
      where
        ds' = map (fmap f) ds

instance Functor Department
  where
    fmap f (Department n m ds es) = Department n m' ds' es'
      where
        m' = fmap f m
        ds' = map (fmap f) ds
        es' = map (fmap f) es

instance Functor Employee
  where
    fmap f (Employee n a s) = Employee n a (f s)

Companies as foldables over salaries

Here are also the Foldable instances -- we exploit the property of the Foldable type class, that the minimal definition in an instance can consist of either foldr or foldMap, as the counterpart and all other members of Foldable are universally predefined; it turns out that foldMap is straightforward to define for companies, even though one could argue that the following code isn't efficient, for example, in terms of the use of concat.

instance Foldable Company
  where
    foldMap f (Company _ ds) = ds'
      where
        ds' = mconcat (map (foldMap f) ds)

instance Foldable Department
  where
    foldMap f (Department _ m ds es) = m' `mappend` ds' `mappend` es'
      where
        m' = foldMap f m
        ds' = mconcat (map (foldMap f) ds)
        es' = mconcat (map (foldMap f) es)

instance Foldable Employee
  where
    foldMap f (Employee _ _ s) = f s

Discussion

The example demonstrates an important limitation of the functorial approach: we need to assume that a data structure can be usefully parameterized in an "element" type of a "container" type. This makes semse for actual container types such as lists or rose trees, but it is not entirely useful for more heterogeneous data structures such as companies breaking down into departments, employees, names, addresses, and salaries. In theory, we could expose "views" on copanies so that the substructure of interested is "exposed" via the type parameter, but such a conversion back and forth between custom views would be both expensive and possibly confusing.

See Contribution:haskellNonfunctorial for a more idiomatoc approach of generalizing cut to a higher-order function. Ultimately, we could leverage Theme:Haskell genericity to perform traversals on heterogeneous data structures; see, for example, Contribution:haskellSyb.


Ralf Lämmel edited this article at Sun, 09 Jul 2023 17:53:52 +0200
Compare revisions Compare revisions

User contributions

    This user never has never made submissions.

    User edits

    Syntax for editing wiki

    For you are available next options:

    will make text bold.

    will make text italic.

    will make text underlined.

    will make text striked.

    will allow you to paste code headline into the page.

    will allow you to link into the page.

    will allow you to paste code with syntax highlight into the page. You will need to define used programming language.

    will allow you to paste image into the page.

    is list with bullets.

    is list with numbers.

    will allow your to insert slideshare presentation into the page. You need to copy link to presentation and insert it as parameter in this tag.

    will allow your to insert youtube video into the page. You need to copy link to youtube page with video and insert it as parameter in this tag.

    will allow your to insert code snippets from @worker.

    Syntax for editing wiki

    For you are available next options:

    will make text bold.

    will make text italic.

    will make text underlined.

    will make text striked.

    will allow you to paste code headline into the page.

    will allow you to link into the page.

    will allow you to paste code with syntax highlight into the page. You will need to define used programming language.

    will allow you to paste image into the page.

    is list with bullets.

    is list with numbers.

    will allow your to insert slideshare presentation into the page. You need to copy link to presentation and insert it as parameter in this tag.

    will allow your to insert youtube video into the page. You need to copy link to youtube page with video and insert it as parameter in this tag.

    will allow your to insert code snippets from @worker.