0
votes

I have a typeclass C.

class C a where
  changeProperty :: a -> ??? -> a

C has two (or more) instances

data A = A AProperty X
data B = B BProperty Y
instance C A where
   changeProperty (A _ x) ap = A ap x
instance C B where
   changeProperty (B _ y) bp = B bp y

bp and ap are of different types. AProperty is only compatible with A and BProperty is only compatible with B. Is it possible to make some for of type signature, or other solution that allows the method changeProperty to accept only these combinations? The only options I have been able to think of either completely separate the methods (in this case into changeAProperty and changeBProperty), or require me to make AProperty and BProperty the same type, which allows for invalid combinations like an A with a BProperty. Ideally I would like to have a Property class of which AProperty and BProperty are instances, but I don't know how to do this and avoid the invalid combinations.

edit: The context of this problem is a script for building molecules for MD simulation. A, B and so on are connections between atoms. For example A would be a bond, containing the parameters for this bond in AProperty and the Atoms that are involved in X. B Would be an angle and BP would contain the parameters related to that angle, Y the Atoms and so on.

3
I am not really convinced this is a good idea in the first place. What will you do in case a data type has two properties, both having the same type? Change one of them (which one?), or both?Willem Van Onsem
You can, if you want define type classes with two or more parameters (by enabling a certain pragma).Willem Van Onsem
I am unsure what you mean by one data type having two properties. To clarify each Property data type contains all the information required for each A and B respectively. The reason I want a separate type for the properties is that they represent rather different things, and I want to be able to have several constructors for each of them.notBob

3 Answers

4
votes

I like the other solutions for their generality. But in this particular case, I'd like to propose a classless solution.

data Meta properties parameters = Meta properties parameters

changeProperty :: Meta props params -> props -> Meta props params
changeProperty (Meta props params) props' = Meta props' params

type A = Meta AProperty X
type B = Meta BProperty Y

This kind of thing is a very common need; see also this question for a brief overview of the record accessor way of achieving this and the libraries folks have built up for reducing the pains associated with record accessors.

3
votes

I reckon you want this:

{-# LANGUAGE TypeFamilies #-}
class C a where
  type CProperty a :: *
  changeProperty :: a -> CProperty a -> a

data A = A AProperty X
data B = B BProperty Y
instance C A where
   type CProperty A = AProperty
   changeProperty (A _ x) ap = A ap x
instance C B where
   type CProperty B = BProperty
   changeProperty (B _ y) bp = B bp y

...or, if indeed AProperty only applies to A, a MPTC solution with functional dependencies:

{-# LANGUAGE FunctionalDependencies #-}

class C a ap | ap -> a, a -> ap where
    changeProperty :: a -> ap -> a

data X = X
data Y = Y

data AProperty = AProperty
data BProperty = BProperty

data A = A AProperty X
data B = B BProperty Y

instance C A AProperty where
    changeProperty (A _ x) ap = A ap x
instance C B BProperty where
    changeProperty (B _ y) bp = B bp y

As you've noticed, this also compiles without the functional dependencies, however that is often cumbersome to use in practice because then for all the compiler knows, A could have any other property and AProperty could apply to any other type, so you end up having to type lots of explicits constraints for the type checker.

1
votes

As Willem Van Onsem mentioned, it is possible to use a multi-parameter type class.

{-# LANGUAGE MultiParamTypeClasses #-}
class C a ap where
    changeProperty :: a -> ap -> a

data X = X
data Y = Y

data AProperty = AProperty
data BProperty = BProperty

data A = A AProperty X
data B = B BProperty Y
instance C A AProperty where
    changeProperty (A _ x) ap = A ap x
instance C B BProperty where
    changeProperty (B _ y) bp = B bp y

As for if it is the best solution I don't know.