Re: [Haskell-cafe] Inheritance and Wrappers

From: Steffen Schuldenzucker
On 01/31/2011 08:58 PM, MattMan wrote:
[...]
data Wrapper a = Wrap a instance (Num a) => AbGroup (Wrapper a) where add (Wrap i) (Wrap j) = Wrap(i+j)
However, this is clumsy. Is there something else I can do? Thanks This is the normal approach. You can do funny things with the OverlappingInstances extension, but it is probably not what you want.
The problem is that the compiler only considers the heads of the instance declarations when determining which instance to use for a specific type. So an instance like this:
instance (Num a) => AbGroup a where ...
means: Some type matching 'a' (that is, any type) is an instance of 'AbGroup' if and only if it is an instance of 'Num'.
I would word this differently. I would say this instance means: Some type matching 'a' (that is, any type) is an instance of 'AbGroup'. It is an error to use AbGroup methods if 'a' does not have a Num instance in scope. The important point is that this declares an AbGroup instance for every type, not just types with Num instances. Which means that this instance is much less useful than you would hope. I would probably use either Henning's approach of separate functions or an automatic deriving mechanism. I've never understood why Haskellers are so averse to writing new instances anyway. John

On 1 February 2011 11:41, John Lato
The important point is that this declares an AbGroup instance for every type, not just types with Num instances.
So, is there a way to declare an AbGroup instance for the types with num instances only? Thanks, Ozgur

On 1 February 2011 11:47, Ozgur Akgun
So, is there a way to declare an AbGroup instance for the types with num instances only?
No - as Henning says its then no more useful than simply a function: add :: (Num u) => a -> a -> a add = (+) 'Overarching instances' i.e. classes with one implementation are a design flaw, not a virtue [*] so this hypothetical extension isn't welcome... {-# LANGUAGE OverarchingInstances #-} [*] Of course, there might be some valid cases for things in Oleg Kiselyov's typecast style, but modelling numericals doesn't look like one of them.

OK, what about this as a use case then. I want to create a type class 'Term'
with only one function in it. The function returns a 'termTag' which labels
the *"kind"* of a value in a DSL.
class Term a where
termTag :: a -> String
A user of this type-class can happily provide an instance without any other
type class requirement. However, I want those types which are instances of
Data to be an instance of Term automatically. On top of that, I don't want
to stop the user from creating a special instance for their data type.
I want to be able to write the following instance to accomplish that:
instance Data t => Term t where
termTag = show . toConstr
And if the user wants to write a more specific instance, they should be
welcome to do so:
instance Term UserDT where
termTag (...) = ...
I am not very much interested in the technical details about how things
currently are, I am more interested in a discussion about why (if?) this
would be considered a *design flaw*?
Best,
On 1 February 2011 12:18, Stephen Tetley
On 1 February 2011 11:47, Ozgur Akgun
wrote: So, is there a way to declare an AbGroup instance for the types with num instances only?
No - as Henning says its then no more useful than simply a function:
add :: (Num u) => a -> a -> a add = (+)
'Overarching instances' i.e. classes with one implementation are a design flaw, not a virtue [*] so this hypothetical extension isn't welcome...
{-# LANGUAGE OverarchingInstances #-}
[*] Of course, there might be some valid cases for things in Oleg Kiselyov's typecast style, but modelling numericals doesn't look like one of them.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Ozgur Akgun

On Tuesday 01 February 2011 13:45:34, Ozgur Akgun wrote:
I want to be able to write the following instance to accomplish that:
instance Data t => Term t where termTag = show . toConstr
And if the user wants to write a more specific instance, they should be welcome to do so:
instance Term UserDT where termTag (...) = ...
I am not very much interested in the technical details about how things currently are, I am more interested in a discussion about why (if?) this would be considered a *design flaw*?
I think the strongest reason to consider it a design flaw is the current implementation, that instance selection doesn't take contexts into account. Things being as they are, an instance A a => B a where ... just isn't very useful. If instance selection took contexts into account, such an instance might be useful, but it would still prevent users from declaring instances for both classes (without an extension which would have much the same problems as OverlappingInstances).

On 1 February 2011 12:45, Ozgur Akgun
I am not very much interested in the technical details about how things currently are, I am more interested in a discussion about why (if?) this would be considered a design flaw?
Wanting a general base case + specific exceptional cases is in no way heinous. However this is somewhat at odds with type-class resolution as it stands. SYB3 is a one of systems to reconcile the problem. [By the way, this needs interpreting with a large grain of salt. I haven't been following the Generics story much recently, so I could be mis-representing things, nor do I have a particularly sound knowledge of how GHC resolves overlapping...]

OK, what about this as a use case then. I want to create a type class 'Term' with only one function in it. The function returns a 'termTag' which labels the
"kind" of a value in a DSL.
class Term a where termTag :: a -> String
A user of this type-class can happily provide an instance without any other type
class requirement. However, I want those types which are instances of Data to be
an instance of Term automatically. On top of that, I don't want to stop the user
from creating a special instance for their data type.
I want to be able to write the following instance to accomplish that:
instance Data t => Term t where termTag = show . toConstr
A much more predictable option is to provide this default implementation as a function termTagData :: (Data t) => (t -> String) and let the library clients use it in their instances if the behavior is fine: instance Term MyT where termTag = termTagData
And if the user wants to write a more specific instance, they should be welcome
to do so:
instance Term UserDT where termTag (...) = ...
I am not very much interested in the technical details about how things currently are, I am more interested in a discussion about why (if?) this would be considered a design flaw?
Here's one thing to consider: Can you write a function f :: (Data a) => a -> String f x = termTag x It would seem the Data a => Term a instance justifies this function, and it will always use the default instance. Now, what happens if "f" is applied to a value of some type T which is an instance of Data, but has a custom Term instance? Brandon.

On 3 February 2011 02:35, Brandon Moore
Here's one thing to consider:
Can you write a function
f :: (Data a) => a -> String f x = termTag x
It would seem the Data a => Term a instance justifies this function, and it will always use the default instance.
Now, what happens if "f" is applied to a value of some type T which is an instance of Data, but has a custom Term instance?
Great point, thanks! I guess somehow you shouldn't be allowed to write that function "f". I need to think about this one. -- Ozgur

On Tue, Feb 1, 2011 at 11:47 AM, Ozgur Akgun
On 1 February 2011 11:41, John Lato
wrote: The important point is that this declares an AbGroup instance for every type, not just types with Num instances.
So, is there a way to declare an AbGroup instance for the types with num instances only?
Not to my knowledge. John
participants (5)
-
Brandon Moore
-
Daniel Fischer
-
John Lato
-
Ozgur Akgun
-
Stephen Tetley