Alternative to newtypes or orphan instances

Lets say there's a type `T` I've imported from a package I don't control. I can easily write new functions to work on `T`. For example, I could write: f :: T -> T -> T f x y = ... I've extended T's functionality. No need for newtypes. This makes sense. Whilst a newtype in the following case: newtype Time = Time Int because in this case `Time` is conceptually different to `Int`. For example, you shouldn't multiply two `Time`s So in summary, we've just extended T's functionality appropriately, without introducing a newtype. But this could be dangerous. Lets say another module writer writes a similar function "f". Then if someone imports their module and ours, there will be a clash. Fortunately the compiler tells about this when it happens, and we can then use qualified imports to workaround the clash. This I'll get back to later. Now lets say I want to extend T's functionality some more, but this time by making it an instance of class C. Class C already exists in a package I don't control. Now it would be unreasonable for every class writer to write instances for every possible data type that is appropriate for their class. As you can see, it's an O(n^2) problem, so there will undoubtably be situations where one needs to write instances for data types and classes in separate packages. One recommended approach here is to newtype T, like the following: newtype MyT = MyT T however I find this incredibly ugly. Conceptually, there is no "newtype", and now there's different types of Ts bashing around which really are the same thing. Not only that, they're incompatible, for no good reason. Instead of these different Ts being different types because they're conceptually different things that shouldn't be mixed, the type of these objects depends solely on what operations I want to apply to them. Imagine a rule where: f :: T -> T -> T was only legal in the module T was defined. If you wanted to define T anywhere else, you'd have to do: f :: MyT -> MyT -> MyT That's how messy the newtype solution is, it wouldn't be accepted for ordinary functions, and I don't think it's reasonable to apply that messiness to class functions. A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them. This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user, directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package. This is okay if you're the only person doing it, but I think it's reasonable to say that if your practices would be bad if someone else was doing them also, then they're a bad practice. Indeed, if someone else writes their own instances and they clash with yours, people simply can't use your package and their package in the same program. This is bad. In the simple "function" case. We deal with the clash of two functions named "f" using qualified imports. We can't do that instances, nor can we even hide one of the instances. So to recap here we've tried: (1) Copying the data type. We've decided this has lots of problems. (2) Just making an orphan instance. This has serious problems too. I want to suggest a third option: (3) Copying the class. What would this involve: a) Making a new class (say C1), with the same class methods as the existing one (say C). b) Making an instance of the class like so: instance (C x) => C1 x where f = ModuleC.f ... which forwards all the calls to C. c) Add your new instances to C1 (these will overlap with the above but they will be more specific so with overlapping instances this is allowed). Now if your users want to use your instances, they import C1. They can still use the data type T as usual on the old instances, or on any existing functions defined on T directly. The big question is, what if someone else does this, and creates C2? Then they'll be a clash. But in this case, classes, unlike instances, can be explicitly imported and qualified. Your packages won't be impossible to use together, just the user will have to import one of them qualified. That's acceptable, we understand we have to do this sometimes with clashing function names too. So I guess my questions are: What do you think of my analysis? Are there parts that are incorrect? What do you think of my solution? Is there reasons why it is not as good as the "newtype" or "orphan instances" approach? Is there a way to make my solution easier to implement, i.e. less typing?

On 24 August 2015 at 09:18, Clinton Mead
A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user, directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something. Erik

The original class still exists, I can't see how making a new class based
on the old on affects that. Won't existing functions in modules which
import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
A second approach is an orphan instance. The recommendation here is to
the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user,
On 24 August 2015 at 09:18, Clinton Mead
javascript:;> wrote: put directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik

I meant that presumably, there are library functions like 'f :: C a =>
...' that you want to use with type T. But copying class C to C2, and
only giving an instance C2 T doesn't allow you to do that.
Erik
On 24 August 2015 at 11:54, Clinton Mead
The original class still exists, I can't see how making a new class based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
wrote: On 24 August 2015 at 09:18, Clinton Mead
wrote: A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user, directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik

If, for your data, you create an instance of the new class — but not the original one, then you can't use all the machinery that expects input being of the old class. Which is the point.
24.08.2015, 12:55, "Clinton Mead"
The original class still exists, I can't see how making a new class based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
wrote: On 24 August 2015 at 09:18, Clinton Mead
wrote: A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user, directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

There's no new data. Class C already exists in another package. Data T
already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C.
b) Just create an orphan instance T of C.
c) Create a new class C1, forward its implementation to C, and add an
instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate
is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm
not sure if that's because I'm misunderstanding you or I'm not explaining
myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
If, for your data, you create an instance of the new class — but not the original one, then you can't use all the machinery that expects input being of the old class. Which is the point.
The original class still exists, I can't see how making a new class
24.08.2015, 12:55, "Clinton Mead"
javascript:;>: based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work? On Monday, 24 August 2015, Erik Hesselink
On 24 August 2015 at 09:18, Clinton Mead
javascript:;> wrote: A second approach is an orphan instance. The recommendation here is to
the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user,
javascript:;> wrote: put directly
uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org javascript:; http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

OK. There is no point in having a class C, if there is no functions to work with it. So, this "other package" likely contains some functions that take types (of that class) as input. Like this:
foo :: C a => a -> Int
You don't need class C if you aren't using any functions from the library. So, with your (c), if you try to do
a :: T
a = ...
b :: Int
b = foo a
you'll get a compile error. THAT is the problem.
24.08.2015, 13:28, "Clinton Mead"
There's no new data. Class C already exists in another package. Data T already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C. b) Just create an orphan instance T of C. c) Create a new class C1, forward its implementation to C, and add an instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm not sure if that's because I'm misunderstanding you or I'm not explaining myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: If, for your data, you create an instance of the new class — but not the original one, then you can't use all the machinery that expects input being of the old class. Which is the point.
24.08.2015, 12:55, "Clinton Mead"
: The original class still exists, I can't see how making a new class based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
wrote: On 24 August 2015 at 09:18, Clinton Mead
wrote: A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user, directly uses one of the instances, they need to import it, and they pollute the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Why does 'foo' fail? The module that 'foo'is defined in still can see C. C
still exists. So as long as I import foo it should still work yes?
On Monday, 24 August 2015, Miguel Mitrofanov
OK. There is no point in having a class C, if there is no functions to work with it. So, this "other package" likely contains some functions that take types (of that class) as input. Like this:
foo :: C a => a -> Int
You don't need class C if you aren't using any functions from the library. So, with your (c), if you try to do
a :: T a = ...
b :: Int b = foo a
you'll get a compile error. THAT is the problem.
There's no new data. Class C already exists in another package. Data T already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C. b) Just create an orphan instance T of C. c) Create a new class C1, forward its implementation to C, and add an instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm not sure if that's because I'm misunderstanding you or I'm not explaining myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
javascript:;> wrote: If, for your data, you create an instance of the new class — but not
24.08.2015, 12:55, "Clinton Mead"
javascript:; :
The original class still exists, I can't see how making a new class
On Monday, 24 August 2015, Erik Hesselink
javascript:;> wrote:
On 24 August 2015 at 09:18, Clinton Mead
javascript:;> wrote: A second approach is an orphan instance. The recommendation here is to put the orphan instances in their own module, so the user can choose to import them.
This may works ok if your user is writing an executable. But what if your user is writing a library themselves. But once, you, or your user,
uses one of the instances, they need to import it, and they pollute
24.08.2015, 13:28, "Clinton Mead"
javascript:;>: the original one, then you can't use all the machinery that expects input being of the old class. Which is the point. based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work? directly the global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
I want to suggest a third option:
(3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org javascript:; http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

After implementing the steps in (c), T is an instance of both C and C1, and
both those instances define foo the same way. The only way I see a problem
if is a module imports both C1 and C unqualified (they'll clash) but I see
no reason to do that.
On Monday, 24 August 2015, Clinton Mead
Why does 'foo' fail? The module that 'foo'is defined in still can see C. C still exists. So as long as I import foo it should still work yes?
On Monday, 24 August 2015, Miguel Mitrofanov
javascript:_e(%7B%7D,'cvml','miguelimo38@yandex.ru');> wrote: OK. There is no point in having a class C, if there is no functions to work with it. So, this "other package" likely contains some functions that take types (of that class) as input. Like this:
foo :: C a => a -> Int
You don't need class C if you aren't using any functions from the library. So, with your (c), if you try to do
a :: T a = ...
b :: Int b = foo a
you'll get a compile error. THAT is the problem.
There's no new data. Class C already exists in another package. Data T already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C. b) Just create an orphan instance T of C. c) Create a new class C1, forward its implementation to C, and add an instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm not sure if that's because I'm misunderstanding you or I'm not explaining myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: If, for your data, you create an instance of the new class — but not
24.08.2015, 12:55, "Clinton Mead"
: The original class still exists, I can't see how making a new class
On Monday, 24 August 2015, Erik Hesselink
wrote:
On 24 August 2015 at 09:18, Clinton Mead
wrote: > A second approach is an orphan instance. The recommendation here is to put > the orphan instances in their own module, so the user can choose to import > them. > > This may works ok if your user is writing an executable. But what if your > user is writing a library themselves. But once, you, or your user, > uses one of the instances, they need to import it, and they pollute
24.08.2015, 13:28, "Clinton Mead"
: the original one, then you can't use all the machinery that expects input being of the old class. Which is the point. based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work? directly the > global instance namespace for anyone that uses their package.
For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
> I want to suggest a third option: > > (3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Mon, Aug 24, 2015 at 12:48 PM, Clinton Mead wrote:
After implementing the steps in (c), T is an instance of both C and C1, and both those instances define foo the same way. The only way I see a problem if is a module imports both C1 and C unqualified (they'll clash) but I see no reason to do that.
It's hard to respond with vague details. It may be in your case that what you say works. But this is not true in the general case. Consider the following simple example. -- | A module out of your control module A where data T = T -- | A module out of your control module B where class Show a where show :: a -> String print :: Show a => a -> IO () -- | A module under your control module C where import A import B class Show' a where show' :: a -> String instance Show' T where show' T = "T" Now, you can use "show' T" anywhere you could use "show T" because the result of both functions is the same (monomorphic) type. But you can't use "print T" from module B because you do not have an instance for Show of T. You could define a function "print' :: Show' a => a -> IO ()", but you are not using the functions that rely on Show (a.k.a. the "machinery" mentioned by Miguel). Thus, you lose out on the common functionality that presumably exists for Show. Regards, Sean

I see the problem now Sean. So in your example, is this correct:
"To use the machinery of 'Show', I have to add and instance Show T. But
that would be an orphan instance. So there is no way for T to use show
machinery without adding an orphan instance (aside from modifying a module
not under your control)?"
Even newtype doesn't seem to help in your example. Show has all this
machinery, but it seems completely unusable without adding an orphan
instance or re-writing other people's modules. Is there no other way? This
situation seems incredibly against re-usability, which I thought was
Haskell's strength. Am I missing something?
On Mon, Aug 24, 2015 at 9:07 PM, Sean Leather
On Mon, Aug 24, 2015 at 12:48 PM, Clinton Mead wrote:
After implementing the steps in (c), T is an instance of both C and C1, and both those instances define foo the same way. The only way I see a problem if is a module imports both C1 and C unqualified (they'll clash) but I see no reason to do that.
It's hard to respond with vague details. It may be in your case that what you say works. But this is not true in the general case. Consider the following simple example.
-- | A module out of your control module A where data T = T
-- | A module out of your control module B where class Show a where show :: a -> String print :: Show a => a -> IO ()
-- | A module under your control module C where import A import B class Show' a where show' :: a -> String instance Show' T where show' T = "T"
Now, you can use "show' T" anywhere you could use "show T" because the result of both functions is the same (monomorphic) type.
But you can't use "print T" from module B because you do not have an instance for Show of T. You could define a function "print' :: Show' a => a -> IO ()", but you are not using the functions that rely on Show (a.k.a. the "machinery" mentioned by Miguel). Thus, you lose out on the common functionality that presumably exists for Show.
Regards, Sean

On Mon, Aug 24, 2015 at 1:37 PM, Clinton Mead wrote:
I see the problem now Sean. So in your example, is this correct:
"To use the machinery of 'Show', I have to add and instance Show T. But that would be an orphan instance. So there is no way for T to use show machinery without adding an orphan instance (aside from modifying a module not under your control)?"
Correct. Even newtype doesn't seem to help in your example. Show has all this
machinery, but it seems completely unusable without adding an orphan instance or re-writing other people's modules. Is there no other way? This situation seems incredibly against re-usability, which I thought was Haskell's strength. Am I missing something?
Orphan instances are one of unfortunate pain points of Haskell, though they are sometimes a necessary “evil.” Erik's first response describes a common technique for publishing and sharing orphan instances. See, for example, https://hackage.haskell.org/packages/search?terms=instances . Regards, Sean

Another issues is there's lots of code polymorphic over an instance, which you lose out on if you simply make a new class rather than newtype to support an alternative instance. For instance, lots of datatypes have a second non-trivial Applicative ([] : ZipList, IO : Concurrently , Either : Validation), and there's lots of code that's polymorphic over Applicative (traverse, Data.Functor.Compose, Data.Functor.Product). Using a new class means you still need some newtype and "instance Applicative2 f => Applicative (WrappedA2 f)" to avoid duplicating all this work, so you don't really escape from needing newtypes in non-trivial examples anyway. Sean Leather-2 wrote
On Mon, Aug 24, 2015 at 1:37 PM, Clinton Mead wrote:
I see the problem now Sean. So in your example, is this correct:
"To use the machinery of 'Show', I have to add and instance Show T. But that would be an orphan instance. So there is no way for T to use show machinery without adding an orphan instance (aside from modifying a module not under your control)?"
Correct.
Even newtype doesn't seem to help in your example. Show has all this
machinery, but it seems completely unusable without adding an orphan instance or re-writing other people's modules. Is there no other way? This situation seems incredibly against re-usability, which I thought was Haskell's strength. Am I missing something?
Orphan instances are one of unfortunate pain points of Haskell, though they are sometimes a necessary “evil.” Erik's first response describes a common technique for publishing and sharing orphan instances. See, for example, https://hackage.haskell.org/packages/search?terms=instances .
Regards, Sean
_______________________________________________ Haskell-Cafe mailing list
Haskell-Cafe@
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- View this message in context: http://haskell.1045720.n5.nabble.com/Alternative-to-newtypes-or-orphan-insta... Sent from the Haskell - Haskell-Cafe mailing list archive at Nabble.com.

T is an instance of both C and C1
Then why the hell do you need C1 at all?
24.08.2015, 13:48, "Clinton Mead"
After implementing the steps in (c), T is an instance of both C and C1, and both those instances define foo the same way. The only way I see a problem if is a module imports both C1 and C unqualified (they'll clash) but I see no reason to do that.
On Monday, 24 August 2015, Clinton Mead
wrote: Why does 'foo' fail? The module that 'foo'is defined in still can see C. C still exists. So as long as I import foo it should still work yes?
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: OK. There is no point in having a class C, if there is no functions to work with it. So, this "other package" likely contains some functions that take types (of that class) as input. Like this:
foo :: C a => a -> Int
You don't need class C if you aren't using any functions from the library. So, with your (c), if you try to do
a :: T a = ...
b :: Int b = foo a
you'll get a compile error. THAT is the problem.
24.08.2015, 13:28, "Clinton Mead"
: There's no new data. Class C already exists in another package. Data T already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C. b) Just create an orphan instance T of C. c) Create a new class C1, forward its implementation to C, and add an instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm not sure if that's because I'm misunderstanding you or I'm not explaining myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: If, for your data, you create an instance of the new class — but not the original one, then you can't use all the machinery that expects input being of the old class. Which is the point.
24.08.2015, 12:55, "Clinton Mead"
: The original class still exists, I can't see how making a new class based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
wrote: > On 24 August 2015 at 09:18, Clinton Mead wrote: >> A second approach is an orphan instance. The recommendation here is to put >> the orphan instances in their own module, so the user can choose to import >> them. >> >> This may works ok if your user is writing an executable. But what if your >> user is writing a library themselves. But once, you, or your user, directly >> uses one of the instances, they need to import it, and they pollute the >> global instance namespace for anyone that uses their package. > > For this reason, I think the recommended course of action is to make a > canonical place for the instance, so that everyone can use it. For > example, if you have a library 'foo' providing T, and a library 'bar' > providing C, put the instance in a new package 'foo-bar' (or > 'bar-foo'). Then everyone can use that one instance, since Haskell is > built on the assumption that every type has one unique instance per > class. > >> I want to suggest a third option: >> >> (3) Copying the class. > > This would make a new distinct class, which means you can't call any > methods which have the original class as the context (f :: C a => a -> > a) since that class won't exist for type T (you are trying to avoid > defining that orphan instance). So I don't think this is usable in > most cases, unless I'm missing something. > > Erik , _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Because your type 'T' doesn't have an 'instance C'.
24.08.2015, 13:45, "Clinton Mead"
Why does 'foo' fail? The module that 'foo'is defined in still can see C. C still exists. So as long as I import foo it should still work yes?
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: OK. There is no point in having a class C, if there is no functions to work with it. So, this "other package" likely contains some functions that take types (of that class) as input. Like this:
foo :: C a => a -> Int
You don't need class C if you aren't using any functions from the library. So, with your (c), if you try to do
a :: T a = ...
b :: Int b = foo a
you'll get a compile error. THAT is the problem.
24.08.2015, 13:28, "Clinton Mead"
: There's no new data. Class C already exists in another package. Data T already exists in another different package.
The options are:
a) Create a newtype MyT and an instance MyT of C. b) Just create an orphan instance T of C. c) Create a new class C1, forward its implementation to C, and add an instance T of C1.
I'm suggesting (c) is best, but I haven't seen this elsewhere, the debate is usually between (a) and (b).
I don't really understand the problems you're proposing with (c), but I'm not sure if that's because I'm misunderstanding you or I'm not explaining myself well.
On Monday, 24 August 2015, Miguel Mitrofanov
wrote: If, for your data, you create an instance of the new class — but not the original one, then you can't use all the machinery that expects input being of the old class. Which is the point.
24.08.2015, 12:55, "Clinton Mead"
: The original class still exists, I can't see how making a new class based on the old on affects that. Won't existing functions in modules which import the old class instead of the new class continue to work?
On Monday, 24 August 2015, Erik Hesselink
wrote: On 24 August 2015 at 09:18, Clinton Mead
wrote: > A second approach is an orphan instance. The recommendation here is to put > the orphan instances in their own module, so the user can choose to import > them. > > This may works ok if your user is writing an executable. But what if your > user is writing a library themselves. But once, you, or your user, directly > uses one of the instances, they need to import it, and they pollute the > global instance namespace for anyone that uses their package. For this reason, I think the recommended course of action is to make a canonical place for the instance, so that everyone can use it. For example, if you have a library 'foo' providing T, and a library 'bar' providing C, put the instance in a new package 'foo-bar' (or 'bar-foo'). Then everyone can use that one instance, since Haskell is built on the assumption that every type has one unique instance per class.
> I want to suggest a third option: > > (3) Copying the class.
This would make a new distinct class, which means you can't call any methods which have the original class as the context (f :: C a => a -> a) since that class won't exist for type T (you are trying to avoid defining that orphan instance). So I don't think this is usable in most cases, unless I'm missing something.
Erik ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
participants (5)
-
Clinton Mead
-
Erik Hesselink
-
htebalaka
-
Miguel Mitrofanov
-
Sean Leather