peroni_santo
peroni_santo

Reputation: 367

how to create data sub types

I want pass a sub data type into function.

An example:

data Car a = Ford | Opel | BMW | Mercedes a deriving (Show)

data Mercedes a = SClass a| CClass a| MClass a


myfunc::Car->String
myfunc a = show a ++ " works correctly"

but I do:

myfunc CClass "x"

it says

not in scope: constructor CClass

Upvotes: 2

Views: 220

Answers (2)

huon
huon

Reputation: 102236

To achieve "sub"-types, one has a few options. The first is just to use more data types, as you are attempting:

data Car a = Ford (Ford a) | Mercedes (Mercedes a) deriving (Show)

data Ford a = ModelT a | Falcon a deriving (Show) -- different Fords models
data Mercedes a = CClass a | SClass a deriving (Show) -- different Mercedes models

Note that being a type constructor is different to being a data constructor, so one can't just use data Car = .. | Mercedes a: this is saying that one of the ways to make a Car String is just by saying Mercedes "a", it has no correlation with the type called Mercedes. Now one can just use a function like:

horsepower :: Car a -> Float
horsepower (Ford (ModelT _)) = 1
horsepower (Ford (Falcon _)) = 500
horsepower (Mercedes (CClass _)) = 300
horsepower (Mercedes (SClass _)) = 400

And your example would be:

myfunc :: Show a => Car a -> String
myfunc a = show a ++ " works correctly"

-- myfunc (Ford (ModelT "A")) => Ford (ModelT "A") works correctly
-- myfunc (Mercedes (CClass "b")) => Mercedes (CClass "b") works correctly

Another way to allow "subtyping" like this is to use a type class (depending on the specifics, this might be a better option). E.g.

class (Show c) => Car c where
    make :: String
    model :: c -> String
    horsepower :: c -> Float
    number_of_seats :: c -> Int
    -- etc.

data Ford a = ModelT a | Falcon a deriving (Show)
instance (Show a) => Car (Ford a) where
    make = "Ford"
    horsepower (ModelT _) = 1
    horsepower (Falcon _) = 500
    -- etc.


data Mercedes a = CClass a | SClass a deriving (Show)
instance (Show a) => Car (Mercedes a) where
    make = "Mercedes"
    horsepower (CClass _) = 300
    horsepower (SClass _) = 400
    -- etc.

Your example of myfunc might be:

myfunc :: (Car c) => c -> String
myfunc c = (make `asTypeOf` c) ++ " " ++ show c ++ " works correctly"

-- myfunc (ModelT "A") => Ford ModelT "A" works correctly
-- myfunc (CClass "b") => Mercedes CClass "b" works correctly

Also note that one must call functions like function (Ford (Ford "a")) (or function (CClass "a")) with parenthesis.

Upvotes: 12

Gabriella Gonzalez
Gabriella Gonzalez

Reputation: 35099

First off, you need to define your car type as follows:

data Car a = ... | Mercedes (MercedesClass a) | ...

data MercedesClass a = CClass a | ...

... and then you can write:

myFunc (Mercedes (CClass "x"))

Upvotes: 5

Related Questions