Reputation: 43
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' with
Double' 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' with
Double' 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
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 Double
s alone—even if the input Gene
was discrete. This is mathematically justifiable because Double
contains all of the Int
egers (which can be made more formal if you want).
In Haskell the function fromIntegral
converts Int
s to other numeric types. We can thus write
geneStateAsDouble :: Gene -> Double
geneStateAsDouble = geneState fromIntegral id
Upvotes: 4