In object-oriented languages (e.g; Java and Python) we can make objects/instances from classes.
In Haskell we can make instances from type-classes, ex:
data ShirtSize = S | M | L -- Here ShirtSize is a enum data-type
class MyEq a where
(==) :: a -> a -> Bool
instance MyEq ShirtSize where -- Here ShirtSize is an instance of the MyEq type-class
S == S = True
M == M = True
L == L = True
_ == _ = False
My question is:
What does instance mean in haskell?
In java we can make instances from classes, but in haskell it seems like instances are types (like ShirtSize) which you can apply type-class functions on (e.g the (==) function from MyEq). Am I right? and also what is an instance in Haskell compared to an instance/object in Java?
In Java, the class system is a way to group similar objects. An instance of a class is an individual object which belongs to that class.
In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class. (That is, until you start considering multiparametric type classes).
Incidentally, a Haskell (monoparametric) class somewhat resembles a Java interface and, by extension, a Java class. Or perhaps a Haskell instance resembles Java class. It's better to view this as a coincidence. Approach the term keeping its mathematical origins in mind. A class is just a bunch of things that belong together, and an instance is one of these things.
If you're interested in explanation of type classes and difference from Java interfaces you should read this post by <❤>. It also explains instances as well.
As for me, I look at instance as connection between data type and interface. data contains some information, class contains methods. data is about data (sorry for tautology) and class is about behavior. When you look at data type you don't see what you can do with it, instead you see what it stores. When you look at class you see what type should be able to do, you don't care what it stores internally. In real programming you actually care about details of implementations and how methods implemented using specific data. So instance just shows you relation between some data and some behavior — how this behavior implemented using given data.
In case you're interested more in model of type classes then read this blog post: http://www.haskellforall.com/2012/05/scrap-your-type-classes.html
You can look at instances as a values! It might blow your mind if you face such definition first time.
In some dependently type languages instances are really values that you can pass to other functions. Take a look at this question:
In Idris, is "Eq a" a type, and can I supply a value for it?
it seems like instances are types (like ShirtSize) which you
can apply type-class functions on (e.g the (==) function from MyEq)
Absolutely correct.
In Haskell a type is a defined structure of data. Every value that exists in Haskell code has a defined type. And a type can be made an instance of a class, which means... actually, hold that thought. I want to talk about functions.
Functions have type signatures, defining which type(s) they can be used on. If a function is defined to work on a particular type, then the function can be used on any value that has that type. If a function is defined to work on a particular class, then it can be used on any value of any of the types that are instances of that class.
When you define a class you describe some minimal set of functions (eg == in your example) which have to be implemented for all the types that want to be instances of that class. The class defines names and signatures for those functions, and that definition means those names and signatures are fixed. They will be the same for every instance of the class.
But the implementations aren't fixed by the class. They can be different for different types. We make a type into an instance of a class by writing an instance statement, in which we can define how those functions will work. If the class provides a default implementation of a function, then the different instance types can override the default and have their own definitions. And if there is no default, then the instance types must have their own definitions.
Now you have a minimal set of functions which can be called with any value of any of the types. And you can write more functions that work by calling those functions, and build up from there.
The idea is really useful, but IMHO the terminology is awful. Saying that these types are instances of a class makes it sound as if they're subtypes or child types, inheriting properties from parent types. But it's not like that at all. Being an instance of a class is like being a member of a club. Lots of different, unrelated types can all be instances of the same class. And one type can be an instance of lots of different unrelated classes, all at the same time.
In Rust, they have the same idea, but with the word 'trait' instead of 'class'. Instead of saying "this type is an instance of that class", they would say "this type implements that trait". I think that gets the idea across much better.
Related
In Haskell, it's possible to add constraints to a type parameter.
For example:
foo :: Functor f => f a
The question: is it possible to negate a constraint?
I want to say that f can be anything except Functor for example.
UPD:
So it comes from the idea of how to map the bottom nested Functor.
Let's say I have Functor a where a can be a Functor b or not and the same rules works for b.
Reasons why this is not possible: (basically all the same reason, just different aspects of it)
There is an open-world assumption about type classes. It isn't possible to prove that a type is not an instance of a class because even if during compilation of a module, the instance isn't there, that doesn't mean somebody doesn't define it in a module “further down the road”. This could in principle be in a separate package, such that the compiler can't possibly know whether or not the instance exists.
(Such orphan instances are generally quite frowned upon, but there are use cases for them and the language doesn't attempt to prevent this.)
Membership in a class is an intuitionistic property, meaning that you shouldn't think of it as a classical boolean value “instance or not instance” but rather, if you can prove that a type is an instance then this gives you certain features for the type (specified by the class methods). If you can't prove that the type is an instance then this doesn't mean there is no instance, but perhaps just that you're not smart enough to prove it. (Read, “perhaps that nobody is smart enough”.)This ties back to the first point: the compiler not yet having the instance available is one case of “not being smart enough”.
A class isn't supposed to be used for making dispatch on whether or not a type is in it, but for enabling certain polymorphic functions even if they require ad-hoc conditions on the types. That's what class methods do, and they can come from a class instance, but how could they come from a “not-in-class instance”?
Now, all that said, there is a way you can kind of fake this: with an overlapping instance. Don't do it, it's a bad idea, but... that's the closest you can get.
Suppose I have the following class:
class P a where
nameOf :: a -> String
I would like to declare that all instances of this class are automatically instances of Show. My first attempt would be the following:
instance P a => Show a where
show = nameOf
My first attempt to go this way yesterday resulted in a rabbit warren of language extensions: I was first told to switch on flexible instances, then undecidable instances, then overlapping instances, and finally getting an error about overlapping instance declarations. I gave up and returned to repeating the code. However, this fundamentally seems like a very simple demand, and one that should be easily satisfied.
So, two questions:
Is there a trivially easy way to do this that I've just missed?
Why do I get an overlapping instances problem? I can see why I might need UndecidableInstances, since I seem to be violating the Paterson condition, but there are no overlapping instances around here: there are no instances of P, even. Why does the typechecker believe there are multiple instances for Show Double (as seems to be the case in this toy example)?
You get the overlapping instances error because some of your instances of P may have other instances of Show and then the compiler won't be able to decide which ones to use. If you have an instance of P for Double, then there you go, you get two instances of Show for Double: yours general one and the one already declared in Haskell's base library. How this error is triggered is correctly stated by #augustss in the comments to your question. For more info see the specs.
As you already know, there is no way to achieve what you're trying without the UndecidableInstances. When you enable that flag you must understand that you're taking over the compiler's responsibility to ensure that there won't arise any conflicting instances. This means that, of course, there mustn't be any other instances of Show produced in your library. This also means that your library won't export the P class, which will erase the possibility of users of the library declaring the conflicting instances.
If your case somehow conflicts with the said above, it's a reliable sign of that there must be something wrong with it. And in fact there is...
What you're trying to achieve is incorrect above all. You are missing several important points about the Show typeclass, distinguishing it from constructs like a toString method of popular OO languages:
From Show's haddock:
The result of show is a syntactically correct Haskell expression containing only constants, given the fixity declarations in force at the point where the type is declared. It contains only the constructor names defined in the data type, parentheses, and spaces. When labelled constructor fields are used, braces, commas, field names, and equal signs are also used.
In other words, declaring an instance of Show, which does not produce a valid Haskell expression, is incorrect per se.
Given the above it just doesn't make sense to declare a custom instance of Show when the type allows to simply derive it.
When a type does not allow to derive it (e.g., GADT), generally you'll still have to stick to type-specific instances to produce correct results.
So, if you need a custom representation function, you shouldn't use Show for that. Just declare a custom class, e.g.:
class Repr a where
repr :: a -> String
and approach the instances declaration responsibly.
The general question is which module structure is more convenient when adding instances for existing objects? Which pros and cons there are?
Let's say I want to add NFData instance for Seq type. I can place it in:
Data.Sequence.Extra (as the same thing is done in the vty package)
Data.Sequence.Instances.NFData (more precise)
Control.DeepSeq.Instances
Control.DeepSeq.Instances.Sequence
It's the case when I don't own neither the type class nor the data type. The other common situation is when I own a type type class and want to add instances for data type(s) from some "heavy" package from Hackage, like OpenGL. Let's say the type class I designed is very light and has no direct relation to OpenGL. I don't want my type class depend on "heavy" package, so I want to place OpenGL instances in a separate module (it's my intuitive feeling, if you have other opinion, let's discuss it). So, what this module should be:
MyClass.Instances.OpenGL
Graphics.Rendering.OpenGL.Extra (together with instances for other classes)
Graphics.Rendering.OpenGL.Instances.MyClass
What is more flexible solution? At some point OpenGL can be replaced with other library, or MyClass can be replaced too. Are there any subtle nuances?
Also, which scheme is better if choose MyClass.Instances variant:
MyClass.Class module for the class itself and basic instances and MyClass module reexports it (and maybe MyClass.Instances)
MyClass module for the class and basic instances, and MyClass.All reexports everything
MyClass module for the class and basic instances and no module for reexporting.
The usual process of deciding where to put an instance goes something like this:
If you're making a new data type and want an instance for an existing class:
Put the instance in the same module as the data type.
If you're making a new type class and want to make instances for existing types.
Put the instances in the same module as the type class.
Both the type class and the data type already exist (your case).
If the instance is sufficiently general, I'd contact the author of either the type class or the data type and try to convince them to add the instance in their package.
Otherwise, create a newtype wrapper and write an instance for that.
Only as a last resort consider making an orphan instance.
At first glance, there obvious distinctions between the two kinds of "class". However, I believe there are more similarities:
Both have different kinds of constructors.
Both define a group of operations that could be applied to a particular type of data, in other words, they both define an Interface.
I can see that "class" is much more concise in Haskell and it's also more efficient. But, I have a feeling that, theoretically, "class" and "abstract class" are identical.
What's your opinion?
Er, not really, no.
For one thing, Haskell's type classes don't have constructors; data types do.
Also, a type class instance isn't really attached to the type it's defined for, it's more of a separate entity. You can import instances and data definitions separately, and usually it doesn't really make sense to think about "what class(es) does this piece of data belong to". Nor do functions in a type class have any special access to the data type an instance is defined for.
What a type class actually defines is a collection of identifiers that can be shared to do conceptually equivalent things (in some sense) to different data types, on an explicit per-type basis. This is why it's called ad-hoc polymorphism, in contrast to the standard parametric polymorphism that you get from regular type variables.
It's much, much closer to "overloaded functions" in some languages, where different functions are given the same name, and dispatch is done based on argument types (for some reason, other languages don't typically allow overloading based on return type, though this poses no problem for type classes).
Apart from the implementation differences, one major conceptual difference is regarding when the classes / type classes as declared.
If you create a new class, MyClass, in e.g. Java or C#, you need to specify all the interfaces it provides at the time you develop the class. Now, if you bundle your code up to a library, and provide it to a third party, they are limited by the interfaces you decided the class to have. If they want additional interfaces, they'd have to create a derived class, TheirDerivedClass. Unfortunately, your library might make copies or MyClass without knowledge of the derived class, and might return new instances through its interfaces thatt they'd then have to wrap. So, to really add new interfaces to the class, they'd have to add a whole new layer on top of your library. Not elegant, nor really practical either.
With type classes, you specify interfaces that a type provides separate from the type definition. If a third party library now contained YourType, I can just instantiate YourType to belong to the new interfaces (that you did not provide when you created the type) within my own code.
Thus, type classes allow the user of the type to be in control of what interfaces the type adheres to, while with 'normal' classes, the developer of the class is in control (and has to have the crystal ball needed to see all the possible things for what the user might want to use the class).
From: http://www.haskell.org/tutorial/classes.html
Before going on to further examples of the use of type classes, it is worth pointing out two other views of Haskell's type classes. The first is by analogy with object-oriented programming (OOP). In the following general statement about OOP, simply substituting type class for class, and type for object, yields a valid summary of Haskell's type class mechanism:
"Classes capture common sets of operations. A particular object may be an instance of a class, and will have a method corresponding to each operation. Classes may be arranged hierarchically, forming notions of superclasses and sub classes, and permitting inheritance of operations/methods. A default method may also be associated with an operation."
In contrast to OOP, it should be clear that types are not objects, and in particular there is no notion of an object's or type's internal mutable state. An advantage over some OOP languages is that methods in Haskell are completely type-safe: any attempt to apply a method to a value whose type is not in the required class will be detected at compile time instead of at runtime. In other words, methods are not "looked up" at runtime but are simply passed as higher-order functions.
This slideshow may help you understand the similarities and differences between OO abstract classes and Haskell type classes: Classes, Jim, But Not As We Know Them.
What are the similarities and the differences between Haskell's TypeClasses and Go's Interfaces? What are the relative merits / demerits of the two approaches?
Looks like only in superficial ways are Go interfaces like single parameter type classes (constructor classes) in Haskell.
Methods are associated with an interface type
Objects (particular types) may have implementations of that interface
It is unclear to me whether Go in any way supports bounded polymorphism via interfaces, which is the primary purpose of type classes. That is, in Haskell, the interface methods may be used at different types,
class I a where
put :: a -> IO ()
get :: IO a
instance I Int where
...
instance I Double where
....
So my question is whether Go supports type polymorphism. If not, they're not really like type classes at all. And they're not really comparable.
Haskell's type classes allow powerful reuse of code via "generics" -- higher kinded polymorphism -- a good reference for cross-language support for such forms of generic program is this paper.
Ad hoc, or bounded polymorphism, via type classes, is well described here. This is the primary purpose of type classes in Haskell, and one not addressed via Go interfaces, meaning they're not really very similar at all. Interfaces are strictly less powerful - a kind of zeroth-order type class.
I will add to Don Stewart's excellent answer that one of the surprising consquences of Haskell's type classes is that you can use logic programming at compile time to generate arbitrarily many instances of a class. (Haskell's type-class system includes what is effectively a cut-free subset of Prolog, very similar to Datalog.) This system is exploited to great effect in the QuickCheck library. Or for a very simple example, you can see how to define a version of Boolean complement (not) that works on predicates of arbitrary arity. I suspect this ability was an unintended consequence of the type-class system, but it has proven incredibly powerful.
Go has nothing like it.
In haskell typeclass instantiation is explicit (i.e. you have to say instance Foo Bar for Bar to be an instance of Foo), while in go implementing an interface is implicit (i.e. when you define a class that defines the right methods, it automatically implements the according interface without having to say something like implement InterfaceName).
An interface can only describe methods where the instance of the interface is the receiver. In a typeclass the instantiating type can appear at any argument position or the return type of a function (i.e. you can say, if Foo is an instance of type Bar there must be a function named baz, which takes an Int and returns a Foo - you can't say that with interfaces).
Very superficial similarities, Go's interfaces are more like structural sub-typing in OCaml.
C++ Concepts (that didn't make it into C++0x) are like Haskell type classes. There were also "axioms" which aren't present in Haskell at all. They let you formalize things like the monad laws.