简体   繁体   中英

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 (eg 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM