Dahan
Dahan

Reputation: 163

Pull out data from inside a constructor

I am completely new to Haskell.

I have several layers of information. Each layer is calculated in a unique way, but on the output I have just lists of typed elements.

layer1 :: MyData -> [Element1]
layer1 mydata = unique calculations

layer2 :: MyData -> [Element2]
layer2 mydata = even more unique calculations, using layer1

layer3 :: MyData -> [Element3]
layer3 mydata = completely genious layer of profound information
.....

So here my N functions cannot be parametrized and should remain detached.

Then I want to run a bunch of similar functions on these layers.

I mights do it in a straightforward way

transform1 :: MyData -> [Int]
transform1 mydata = zipWith f (layer1 mydata) constList
transform2 :: MyData -> [Int]
transform2 mydata = zipWith f (layer2 mydata) constList
transform3 :: MyData -> [Int]
transform3 mydata = zipWith f (layer3 mydata) constList
......

But here clearly it is inviting to make some kind of unification. Like

universalTransform :: Layer -> [Int]
universalTransform layer = zipWith f layer constList

Ok. So now I need to make Layer abstraction

data Layer = Lay1 [Element1] | Lay2 [Element2] | Lay3 [Element3]
data LayerName = Lay1Name | Lay2Name | Lay3Name

Then I need to make a dispatching collector

take :: LayerName -> MyData -> Layer
take lname mydata = case lname of
  Lay1Name -> Lay1 (layer1 mydata)
  Lay2Name -> Lay2 (layer2 mydata)
  Lay3Name -> Lay3 (layer3 mydata)

And somehow it becomes huger and huger and the worst problem is that my zipWith f wants a list. And in universalTransform layer is not a list. It is a Layer abstraction. I was trying to befree [ElementsX] from inside of Layer but didn't succeed.

I know I am looking in a wrong direction.

So: how can I do that unification, but in a proper way, and not like ...ehmmm... me myself.

Upvotes: 2

Views: 144

Answers (1)

duplode
duplode

Reputation: 34378

As Alexis King points out, you don't need the Layer type to abstract over the transformations. Just have your function take f and the layer function as arguments. That way, you go from...

transform1 :: MyData -> [Int]
transform1 mydata = zipWith f (layer1 mydata) constList

... to (let's say the type of constList is [Foo]):

transform :: (a -> Foo -> Int) -> (MyData -> [a]) -> MyData -> [Int]
transform f layer mydata = zipWith f (layer mydata) constList

This transform becomes easier on the eyes if you flip the order of the arguments of f:

transform :: (Foo -> a -> Int) -> (MyData -> [a]) -> MyData -> [Int]
transform f layer mydata = zipWith f constList (layer mydata)

Now it becomes easy to not mention mydata explicitly (this final step is perhaps a matter of taste, but I like it best this way):

transform :: (Foo -> a -> Int) -> (MyData -> [a]) -> MyData -> [Int]
transform f layer = zipWith f constList . layer

The main substantial difference between transform and what you were speculating about doing with Layer is that with this solution you are not constrained to just three ways of creating the layers, as specified by LayerName. Given how you stated your problem, however, I guess you didn't really want such a restriction in the first place.


P.S.: If you actually wanted something similar to your Layer proposal (which, again, is something I strongly suspect you don't actually need), you would probably have better luck with something like...

data Element = Elem1 Element1 | Elem2 Element2 | Elem3 Element3

... plus a layer-producing function (that actually produces a list!):

toLayer :: LayerName -> MyData -> [Element]

The function you give zipWith would then be responsible for doing case analysis on Element:

f :: Foo -> Element -> Int

Upvotes: 3

Related Questions