Creating convenient type
I am beginning with Haskell. I have a situation where it is convenient to work the following type synonyms:
type Adult = Int
type Youth = Int
However I can not overload functions on Adult
and Youth
even if they had been synonyms for different types so have to have two seperate versions of functions eg. doSomethingForAdult
and doSomethingForYouth
, so next I tried
data Person = Adult Int | Youth Int
Then I can pattern match and use a single version of functions,
but then I loose the option of using the Adult
and Youth
as types in functions declarations which is convenient. Is there a middle way ? I looked at Either
, but from the description in tutorials it seems this would be misuse ? Something similar to a small type hierarchy with Person
at the root and Youth
and Adult
derived and sti开发者_StackOverflowll being synonyms for Int
would be perfect but I can not figure out how.
I don't see how that would be a misuse of Either
. Just as (,)
is a generic product type, Either
is perfectly acceptable as a generic sum type, i.e. anything of the form:
data T = T1 A | T2 B C
...can be thought of algebraically as A + (B * C)
, which is equivalent to Either A (B, C)
.
On the other hand, if you want to distinguish between Adult
and Youth
, using synonyms for the same actual type can be counterproductive; they're just transparent aliases. An alternate approach would be to use newtype
s, like this:
newtype Adult = Adult Int deriving (Eq, Ord, Show, Read, Num)
newtype Youth = Youth Int deriving (Eq, Ord, Show, Read, Num)
Adjust the deriving
clause to taste; I added Num
here because the underlying type is numeric, but if all you want is a unique identifier then adding or multiplying them doesn't make sense. At this point you can then use a sum type for Person
if you like, or define a type class to get proper overloading:
class Person a where
-- (etc...)
instance Person Adult where -- ...
instance Person Youth where -- ...
Any functions defined in Person
are then effectively overloaded on Adult
and Youth
, letting you dispatch based on type the way "overloaded" functions in other languages do.
Are you wanting a typeclass
?
data Adult = Adult Int
data Youth = Youth Int
class Person a where
doSomething :: a -> b
instance Person Adult where
doSomething (Adult i) = ...
instance Person Youth where
doSomething (Youth i) = ...
This is the typical manner of overloading function in Haskell. The typeclass, Person
, has a single function, doSomething :: a -> b
. Each data type you want to be an instance of Person
can have its own, separate, implementation. If you want the underlying implementation to be the same then just use another function, doSomethingGlobal :: Int -> b
and let each instance equal this new function.
You can have both:
type Adult = Int
type Youth = Int
data Person = Adult Adult | Youth Youth
Data constructors are in a separate namespace from types so there is no conflict here.
I would suggest to just add the type synonym
type Person = Int
and you are able to give types to functions that work both, for Adults abd Youth, like
doSomething :: Person -> ...
(and you keep the possibility to use Adult
and Youth
in other type signatures)
I suggest to make Person
polymorphic:
data Person a = Person a Int
With this definition you can write general functions on Person
level that don't care about the concrete value of a
:
getInt :: Person a -> Int
getInt (Person _ i) = i
incInt :: Person a -> Person a
incInt (Person p i) = Person p (inc i)
Then you can define "tags" to substantiate a Person
:
data Adult = Adult
data Youth = Youth
Now you can write functions for a specific type of Person
rock :: Person Youth -> String
rock (Person Youth k) = "I rock until " ++ show k ++ " in the morning!!!"
精彩评论