GreenQuestor
GreenQuestor

Reputation: 43

Accessing functions for custom Haskell data types

I have searched and searched for the following, in stack overflow in particular and Google in general. My abject apologies if it is either already covered or so trivial as not to be mentioned anywhere.

I have defined a custom data type for objects that are sometimes discrete and sometimes continuous, like so:

data Gene =  Discrete String Int | Continuous String Double 
                 deriving (Eq, Show, Read, Ord)

Here, the String represents the name of the Gene (e.g. vWF or some such) and the numerical parameter is its state, either discrete or continuous, like so:

bober = Discrete "vWF" 2
slick = Continuous "gurg" 0.45432

I could use record syntax to access the properties of a Gene, but then there are 2 different functions for name and state. What I would like is one function to access identity, and one to access state. For identity, this is straightforward, since that is a string for both value constructors:

geneName :: Gene -> String
geneName (Discrete dName _) = dName
geneName (Continuous cName _) = cName

When I try to make a function that return the state of a Gene though, I run into trouble. I thought pattern matching would work:

geneState :: Num a => Gene -> a
geneState (Discrete _ dState) = dState
geneState (Continuous _ cState) = cState

This fails to load in GHCi, giving:

DynamicalModularity.hs:34:35: Couldn't match type Int' withDouble' Expected type: a Actual type: Double In the expression: cState In an equation for `geneState': geneState (Continuous _ cState) = cState Failed, modules loaded: none.

I tried using case syntax:

geneState :: Num a => Gene -> a
geneState gene = case gene of (Discrete _ dState) -> dState
                              (Continuous _ cState) -> cState

Again this doesn't load:

DynamicalModularity.hs:30:56: Couldn't match type Int' withDouble' Expected type: a Actual type: Double In the expression: cState In a case alternative: (Continuous _ cState) -> cState In the expression: case gene of { (Discrete _ dState) -> dState (Continuous _ cState) -> cState } Failed, modules loaded: none.

My question is: Is what I want to do possible and/or good Haskell? Am I missing something obvious? I have been searching for a solution for some time now. Any help would be greatly appreciated.

Upvotes: 3

Views: 148

Answers (1)

J. Abrahamson
J. Abrahamson

Reputation: 74374

Any code which would consume the result of calling geneState would need to be able to handle both Int and Double—clearly this is the case because I can call geneState on both discrete and continuous values.

Let's represent the part of this code which consumes the Int and the Double separately. Both of these parts must be simple functions so we can write them as

intConsumer    :: Int    -> result
doubleConsumer :: Double -> result

Now I have both of these parts return the same result because the consuming code must always return the same kind of thing regardless of whether it receives a discrete or a continuous Gene.

Now, we can write geneState using this information and pattern matching

geneState :: (Int -> result) -> (Double -> result) -> Gene -> result
geneState intConsumer doubleConsumer (Discrete   _ st) = intConsumer    st
geneState intConsumer doubleConsumer (Continuous _ st) = doubleConsumer st

One way we might simplify this function is to assume that all of our consumers function on Doubles alone—even if the input Gene was discrete. This is mathematically justifiable because Double contains all of the Integers (which can be made more formal if you want).

In Haskell the function fromIntegral converts Ints to other numeric types. We can thus write

geneStateAsDouble :: Gene -> Double
geneStateAsDouble = geneState fromIntegral id

Upvotes: 4

Related Questions