Fw: [Haskell-cafe] The difficulty of designing a sequence class

David Menendez wrote:
Brian Hulley writes:
1) Did Edison choose MonadPlus just because this fitted in with the lack of multi-parameter typeclasses in H98?
Instances of Monoid (and your ISeq) have kind *. Instances of MonadPlus (and Edison's Sequence) have kind * -> *.
Functions like map, zip, and their variants are best defined in terms of type constructors.
With Sequence, you have
zipWith :: (Sequence s) => (a -> b -> c) -> s a -> s b -> s c
With ISeq, you'd have to do something like
zipWith :: (ISeq s1 a, ISeq s2 b, ISeq s3 c) => (a -> b -> c) -> s1 -> s2 -> s3
which isn't able to make any assumptions about s1, s2, and s3 having the same structure.
On the other hand it's more powerful because they can now have different structures ie was there any reason not to have: zipWith :: (Sequence s1, Sequence s2, Sequence s3) => (a -> b -> c) -> s1 a -> s2 b -> s3 c
3) Is it worth bothering to derive ISeq from Monoid (with the possible extra inefficiency of the indirection through the definitions for append = mappend etc or does the compiler completely optimize this out)?
I would expect the compiler to inline append.
Thanks - that's good news. I' probably still too much in C++ mode.
4) Would it be worth reconsidering the rules for top level names so that class methods could always be local to their class (ditto for value constructors and field names being local to their type constructor).
Qualified module imports are the way to go, here. Do you really want to start writing "if x Eq/== y Num/+ 1 then ... "?
I'm beginning to see that qualified module imports are indeed the only way to go, because the methods in a type class are only the "virtual" methods - often there are many other "methods" which are put outside the class to save space in the dictionary but which conceptually belong to the class thus putting the class + these extra functions in a single module wraps everything up into a conceptual unit. eg: module Foldable ( Foldable(..) , reduceR ) where class Foldable c a | c -> a where foldR :: (a -> b -> b) -> b -> [a] -> b -- ... reduceR :: Foldable c a => (a -> b -> b) -> (c -> b -> b) reduceR f xs y = foldR f y xs forms the single conceptual unit to use Foldable.foldR, Foldable.reduceR etc so I'll have to retract my suggestion as regards classes... (Although I'm still concerned about value constructors and field names being in the top level instead of local to their type but changing this would require some changes to type inference (so that the constructors and field names could be used unqualified when the type at the given position is known eg by a top level type signature for the function or value) so that's more of a long term idea.) Regards, Brian. -- Logic empowers us and Love gives us purpose. Yet still phantoms restless for eras long past, congealed in the present in unthought forms, strive mightily unseen to destroy us. http://www.metamilk.com

Brian Hulley wrote:
David Menendez wrote:
Brian Hulley writes:
4) Would it be worth reconsidering the rules for top level names so that class methods could always be local to their class (ditto for value constructors and field names being local to their type constructor).
Qualified module imports are the way to go, here. Do you really want to start writing "if x Eq/== y Num/+ 1 then ... "?
I'm beginning to see that qualified module imports are indeed the only way to go,
One reason I forgot: Suppose person A writes ClassA which uses "foo" as a method name, and somewhere else, person B writes ClassB which also uses "foo" as a method name, and both classes become widely used for several years. Now the problem is that person C may come along and notice that there is a useful abstraction to be made by inheriting both from ClassA and ClassB. But both of these define "foo" and there is no mechanism in the language to resolve this. The language C#, which was designed from the outset for programming in the large, already had a solution in the very first release of C#, namely that the interface name could be used to qualify a method name in cases of ambiguity, so transposing this to Haskell, you'd have something like: class ClassA a where foo :: a -> Int class classB a where foo :: a -> String class (ClassA a, ClassB a) => ClassC a where bar :: a -> (Int, String) bar x = (ClassA#foo x, ClassB#foo x) As I see it, Haskell, great and innovative as it is, is still stuck in "programming in the small" and some of the mechanisms needed for programming in the large are not yet available - it is as impossible to ensure that there will never be conflicts between names of class methods as it is to ensure that there will never be conflicts between module names in packages written by different groups of people, and languages like Java and C# solved these problems right at the beginning but Haskell for some reason has ignored the issues, only recently just starting to address the package module name conflict problem for example even though the language has been around for more than a decade. I'm also wondering if it would be a good idea to be able to declare some class methods as final, so they don't clutter up the dictionary at runtime, and so that we could end the dubious practice of declaring some functions which are conceptually part of a class as top level functions outside the class just to save space/time in the dictionary and therefore needing the physical module to create the conceptual grouping instead of using the language level grouping provided by the class name. Anyway these are probably more long term ideas but I mentioned them now just to hopefully start the ball rolling (the above should not be taken as a criticism of Haskell, I'm just saying that at some point we need all the normal mechanisms that everyone else (Java, C#) takes for granted because there's no point waiting till we encounter the same well-known software engineering problems that already have well established good solutions). Regards, Brian. -- Logic empowers us and Love gives us purpose. Yet still phantoms restless for eras long past, congealed in the present in unthought forms, strive mightily unseen to destroy us. http://www.metamilk.com

On Tue, Aug 01, 2006 at 02:56:21AM +0100, Brian Hulley wrote:
Now the problem is that person C may come along and notice that there is a useful abstraction to be made by inheriting both from ClassA and ClassB. But both of these define "foo" and there is no mechanism in the language to resolve this.
This is not true at all. every name in haskell can be uniquely specified.
module ClassA where class ClassA a where foo :: a -> Int
module ClassB where class classB a where foo :: a -> String
import ClassA import ClassB class (ClassA a, ClassB a) => ClassC a where bar :: a -> (Int, String) bar x = (ClassA.foo x, ClassB.foo x)
I'm also wondering if it would be a good idea to be able to declare some class methods as final, so they don't clutter up the dictionary at runtime, and so that we could end the dubious practice of declaring some functions which are conceptually part of a class as top level functions outside the class just to save space/time in the dictionary and therefore needing the physical module to create the conceptual grouping instead of using the language level grouping provided by the class name.
I think a fundamental thing you are missing is that Haskell classes are not like C# or Java or other OO classes. Not because of implementation, but rather they are actually fundamentally different things. The reasons people don't place certain functions in classes has nothing to do with the size of class dicionaries. Heck, jhc doesn't even use dictionaries at all, there is no cost for adding methods to a class. People place them in top level functions because it makes more sense. of course, sometimes it is gotten wrong, and something would have been better off as a class method, but in general there are different concerns when dealing with haskell classes than OO classes. An OO class could be considered equivalent to a triplet of a Haskell data type, a Haskell existential with a class constraint, and a class with the resriction the class type can _only_ appear as the first argument to each method. In haskell all of these things are separate independent tools and are much more general and powerful than the limited and conjoined form that OO programming provides.
Anyway these are probably more long term ideas but I mentioned them now just to hopefully start the ball rolling (the above should not be taken as a criticism of Haskell, I'm just saying that at some point we need all the normal mechanisms that everyone else (Java, C#) takes for granted because there's no point waiting till we encounter the same well-known software engineering problems that already have well established good solutions).
It is best to think of haskell primitives as something completely new, they reuse some naming conventions from OO programming, but that doesn't mean they suffer from the same limitations. It took me a few trys to wrap my brain around it. I liken learning haskell to tipping over a vending machine. you can't just push it, you gotta rock it back and forth a few times building up momentum until bam! suddenly the flash of insight hits and it all makes sense. John -- John Meacham - ⑆repetae.net⑆john⑈

Hello John, Tuesday, August 1, 2006, 6:27:29 AM, you wrote:
It is best to think of haskell primitives as something completely new, they reuse some naming conventions from OO programming, but that doesn't mean they suffer from the same limitations. It took me a few trys to wrap my brain around it. I liken learning haskell to tipping over a vending machine. you can't just push it, you gotta rock it back and forth a few times building up momentum until bam! suddenly the flash of insight hits and it all makes sense.
btw, http://homepages.cwi.nl/~ralf/gpce06/ can help on this way. i think that there is also other papers that can help too but i also agree with Brian - there is some constraints in Haskell comparing to C++ and vice versa. while developing Streams library i was many times bitten by these restrictions -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Jul 31, 2006, at 10:27 PM, John Meacham wrote: [snip]
It is best to think of haskell primitives as something completely new, they reuse some naming conventions from OO programming, but that doesn't mean they suffer from the same limitations. It took me a few trys to wrap my brain around it. I liken learning haskell to tipping over a vending machine. you can't just push it, you gotta rock it back and forth a few times building up momentum until bam! suddenly the flash of insight hits and it all makes sense.
Do you have a lot of personal experience attaining zen-like insight by tipping over vending machines? I'll have to try that some time ;-) *chucke* Thanks for making me laugh this morning.
John
Rob Dockins Speak softly and drive a Sherman tank. Laugh hard; it's a long way to the bank. -- TMBG

From: haskell-cafe-bounces@haskell.org [mailto:haskell-cafe-bounces@haskell.org] On Behalf Of John Meacham
I liken learning haskell to tipping over a vending machine. you can't just push it, you gotta rock it back and forth a few times building up momentum until bam! suddenly the flash of insight hits and it all makes sense.
John
This reminds me of the Simpson's episode where Homer attacks a vending machine and it falls on him, crushing him while at the same time spewing chocolate into his mouth. He falls into a coma. Haskell can be like that too, some days. Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

John Meacham wrote:
On Tue, Aug 01, 2006 at 02:56:21AM +0100, Brian Hulley wrote:
Now the problem is that person C may come along and notice that there is a useful abstraction to be made by inheriting both from ClassA and ClassB. But both of these define "foo" and there is no mechanism in the language to resolve this.
This is not true at all. every name in haskell can be uniquely specified.
module ClassA where class ClassA a where foo :: a -> Int
module ClassB where class classB a where foo :: a -> String
import ClassA import ClassB class (ClassA a, ClassB a) => ClassC a where bar :: a -> (Int, String) bar x = (ClassA.foo x, ClassB.foo x)
Hi John - Thanks for pointing this out. The only thing that I find slightly uneasy about it is that you have to keep the module names and class names in sync so that "ClassA.foo" which means "the value foo exposed by the module ClassA" is in truth the same as "the foo method of class ClassA". The language doesn't enforce this correspondence (indeed perhaps such a correspondence would be undesirable but it's difficult to get used to this need to keep different entities in sync instead of just being able to name the concept once in one place).
I'm also wondering if it would be a good idea to be able to declare some class methods as final, so they don't clutter up the dictionary at runtime, and so that we could end the dubious practice of declaring some functions which are conceptually part of a class as top level functions outside the class just to save space/time in the dictionary and therefore needing the physical module to create the conceptual grouping instead of using the language level grouping provided by the class name.
I think a fundamental thing you are missing is that Haskell classes are not like C# or Java or other OO classes. Not because of implementation, but rather they are actually fundamentally different things.
The reasons people don't place certain functions in classes has nothing to do with the size of class dicionaries. Heck, jhc doesn't even use dictionaries at all, there is no cost for adding methods to a class. People place them in top level functions because it makes more sense. of course, sometimes it is gotten wrong, and something would have been better off as a class method, but in general there are different concerns when dealing with haskell classes than OO classes.
An OO class could be considered equivalent to a triplet of a Haskell data type, a Haskell existential with a class constraint, and a class with the resriction the class type can _only_ appear as the first argument to each method. In haskell all of these things are separate independent tools and are much more general and powerful than the limited and conjoined form that OO programming provides.
I think the transition from OOP to Haskell is difficult because although OOP is less powerful for the reasons you've mentioned, many of the decisions you have to make when writing Haskell code just don't exist in OOP eg in C# there are no modules to worry about, every class belongs to a file with the same name, you don't need to decide where to put the object argument and there are no top level functions or values. In contrast, in Haskell you have to juggle class declarations, instance declarations, types, values which may or may not be part of a class, and decide which combinations of the above should go into which modules and what the names of the modules should be and manually remember to keep some module names and class names (or type names eg Data.Set and data Set a = ...) in sync.
Anyway these are probably more long term ideas but I mentioned them now just to hopefully start the ball rolling (the above should not be taken as a criticism of Haskell, I'm just saying that at some point we need all the normal mechanisms that everyone else (Java, C#) takes for granted because there's no point waiting till we encounter the same well-known software engineering problems that already have well established good solutions).
It is best to think of haskell primitives as something completely new, they reuse some naming conventions from OO programming, but that doesn't mean they suffer from the same limitations.
Hopefully my neural pathways will reconnect themselves soon to take advantage of all this new power!!! :-)
It took me a few trys to wrap my brain around it. I liken learning haskell to tipping over a vending machine. you can't just push it, you gotta rock it back and forth a few times building up momentum until bam! suddenly the flash of insight hits and it all makes sense.
Great image! We need a film or video of "a day in the life of Haskell" with these kind of things in it eg camera cutting from scene of vending machine satori to animated folds rippling through space gathering structure like a new planet forming, then another cut to a silent robed figure casting objects from a sack into a bottomless pit... :-) Best regards, Brian. -- Logic empowers us and Love gives us purpose. Yet still phantoms restless for eras long past, congealed in the present in unthought forms, strive mightily unseen to destroy us. http://www.metamilk.com
participants (5)
-
Bayley, Alistair
-
Brian Hulley
-
Bulat Ziganshin
-
John Meacham
-
Robert Dockins