tags:

views:

204

answers:

4

Is it possible to write something like:

data SomeData = SomeValue | (Integral a) => SomeConstructor a

And how exactly would one write this?

+6  A: 

For example, using GADTs:

{-# LANGUAGE GADTs #-}
data SomeData
    where
    SomeValue :: SomeData
    SomeConstructor :: Integral a => a -> SomeData

Example of usage:

*Main> :t SomeValue 
SomeValue :: SomeData

*Main> :t SomeConstructor 15
SomeConstructor 15 :: SomeData

*Main> :t SomeConstructor "aaa"

<interactive>:1:0:
    No instance for (Integral [Char])
      arising from a use of `SomeConstructor' at <interactive>:1:0-20
    Possible fix: add an instance declaration for (Integral [Char])
    In the expression: SomeConstructor "aaa"

*Main> let x = SomeConstructor 15 in case x of { SomeConstructor p -> fromIntegral p :: Int }
15
Roman Cheplyaka
There is no need for GADT there, I guess. They are more useful with `data OtherData a where { IntData :: Integral a => a -> OtherData a; EmptyData :: OtherData EmptyType`. I.e. when you need to produce some specific (sometimes derivated) types by different constructors.
ony
A: 

You can do something like this:

data Integral a => SomeData a =
    SomeValue
    | SomeConstructor a
Daniel Pratt
Of course it depends on what OP really wants to achieve. Your SomeData is parameterized by type `a` while his isn't. Also it looks like he wants to apply different restrictions to different constructors.
Roman Cheplyaka
If SomeData didn't want to be parameterized, you could pull SomeValue out into a separate type and join them via Either, couldn't you?
Owen S.
Again, if you do that, you'll have Either parameterized by the type `a`. For instance, you would not be able to have a list consisting of SomeData made from different Integrals.Unparameterized container is "polymorphic" container -- you can put there anything (which obey its restrictions) and it won't affect its type.
Roman Cheplyaka
Also you can write `data SomeData a = SomeValue | Integral a => SomeConstructor a` to allow `SomeValue` constructor be more polymorphic than `SomeConstructor a`. That's closer to orignal example, I guess.
ony
+11  A: 

This is similar to Daniel Pratt's answer, but the more typical approach is to leave off the type constraints on your data definition, like this:

data SomeData a = SomeValue
                | SomeConstructor a

Instead, you should put the (Integral a) constraint on any functions that require it, which you'd have to do even if you added the constraint to the data definition too. Putting the constraint on the data definition buys you nothing, but forces you to carry around the constraint on all uses of SomeData, even those that don't care what a is at all. See Chapter 10 of Real World Haskell for more.

Paul Kuliniewicz
Note that, unlike class constraints on ordinary data declarations, GADTs *do* make available a class constraint, so you wouldn't have to explicitly put it on your function.
Antal S-Z
GADTs, like the other ways of constraining types on data definitions, are not usually used that way though. In general, if you just want to restrict the type of the values, it is usually more flexible andconvenient to that with the functions that use it.GADTs are more useful for the opposite - when you need to *increase*the types that can occur as parameters, and pattern-match against that.
Yitz
@Antal S-Z: I guess you are confused by `data SomeClass a => SomeData a where SomeData :: a -> SomeData a` and `data SomeData a where SomeData :: SomeClass a => a -> SomeData a`. In first case you also need to add context `f :: SomeClass a => SomeData a` just to use `a` in declaration of polymorphic type `SomeData a`.
ony
ony
@ony: I understand that; what I was trying to say is that GADTs [as used by Roman](http://stackoverflow.com/questions/3181965/haskell-and-conditional-data-structures/3182002#3182002) don't require the context. It might be more accurate to say that only class constraints within the GADT are made available; class constraints on the datatype as a whole aren't treated any differently.
Antal S-Z
what I, of course, was really hoping for was to make the class constraint implicit in some way.
Pepijn
+2  A: 

Yep, exactly as you want, but need to mention quantification:

{-# LANGUAGE ExistentialQuantification #-}

data SomeData = SomeValue
              | forall a . Integral a => SomeConstructor a
ony