Checking Sig Equality in Alloy - equality

In the following Alloy model I want to check the equality of two instances of a sig with Bool field type:
module test
open util/boolean as bool
sig Info {
active: Bool
}
assert assertion {
all x1, x2: Info |
x1.active = True && x2.active = True implies x1 = x2
}
check assertion for 10
This model checks for equality of x_1 and x_2 if both have True as their active field. Alloy comes back with a counterexample, however, in the counterexample, both x_1 and x_2 are structurally equal but for some reason Alloy considers them not equal.
Edit:
One suggestion is to use subtyping as follows:
sig Info {}
sig ActiveInfo in Info {}
-- i is inactive if i in (Info - ActiveInfo)
However, this is not suitable for my model.
Quote from the Software Abstractions book:
"
Is equality structural equality or reference equality?
A relation has no identity distinct from its value, so this distinction,
based on programming notions, doesn’t make sense here. If two relations have the same set of tuples, they aren’t two relations: they’re just
one and the same relation. An atom is nothing but its identity; two atoms are equal when they are the same atom. If you have a set of atoms
that represent composite objects (using some relations to map the atoms to their contents), you can define any notion of structural equality
you want explicitly, by introducing a new relation. (And for those C++
programmers out there: no, you can’t redefine the equals symbol in Alloy.)"
I don't quite understand this paragraph. I appreciate explanation on how equality works in Alloy. Particularly on how to check equalities of atoms with different identities but same values?

I know that equality in Alloy is based on values.
This is not true. x1 and x2 have the same value for active, but are different atoms in the signature. It's similar to how in a lot of OOP languages, two objects can have the same structural values but have different identities.
Incidentally, I'd recommend using subtypes to represent booleans. You could do
sig Info {}
sig ActiveInfo in Info {}
-- i is inactive if i in (Info - ActiveInfo)

You can think of a field in a signature as "belonging" to an atom of that signature if you like, but it's better just to think of fields as relations. You wouldn't expect two persons to be the same if they have the same mother:
sig Person {mother: Parent}
But if you want your signature to have the property that no two distinct members have the same fields, you can just add that as a fact:
sig Coordinate {x, y: Value}
fact {all c, c': Coordinate | (c.x = c'.x and c.y = c'.y) implies c = c'}

Related

Restructuring an OOP datatype into Haskell types

Coming from an OOP background, Haskell's type system and the way data constructors and typeclasses interact is difficult to conceptualize. I can understand how each are used for simple examples, but some more complication examples of data structures that are very well-suited for an OOP style are proving non-trivial to translate into similarly elegant and understandable types.
In particular, I have a problem with organizing a hierarchy of data such as the following.
This is a deeply nested hierarchical inheritance structure, and the lack of support for subtyping makes it unclear how to turn this structure into a natural-feeling alternative in Haskell. It may be fine to replace something like Polygon with a sum data type, declaring it like
data Polygon
= Quad Point Point
| Triangle Point Point Point
| RegularNGon Int Radius
| ...
But this loses some of the structure, and can only really satisfactorily be done for one level of the hierarchy. Typeclasses can be used to implement a form of inheritance and substructure in that a Polygon typeclass could be a subclass of a Shape, and so maybe all Polygon instances have implementations for centroid :: Point and also vertices :: [Point], but this seems unsatisfactory. What would be a good way of capturing the structure of the picture in Haskell?
You can use sum types to represent the entire hierarchy, without losing structure. Something like this would do it:
data Shape = IsPoint Point
| IsLine Line
| IsPolygon Polygon
data Point = Point { x :: Int, y :: Int }
data Line = Line { a :: Point, b :: Point }
data Polygon = IsTriangle Triangle
| IsQuad Quad
| ...
And so on. The basic pattern is you translate each OO abstract class into a Haskell sum type, with each of its immediate OO subclasses (that may themselves be abstract) as variants in the sum type. The concrete classes are product/record types with the actual data members in them.1
The thing you lose compared to the OOP you're used to by modeling things this way isn't the ability to represent your hierarchy, but the ability to extend it without touching existing code. Sum types are "closed", where OO inheritance is "open". If you later decide that you want a Circle option for Shape, you have to add it to Shape and then add cases for it everywhere you pattern match on a Shape.
However, this kind of hierarchy probably requires fairly liberal downcasting in OO. For example, if you want a function that can tell if two shapes intersect that's probably an abstract method on Shape like Shape.intersects(Shape other), so each sub-type gets to write its own implementation. But when I'm writing Rectangle.intersects(Shape other) it's basically impossible generically, without knowing what other subclasses of Shape are out there. I'll have to be using isinstance checks to see what other actually is. But that actually means that I probably can't just add my new Circle subclass without revisiting existing code; an OO hierarchy where isinstance checks are needed is de-facto just as "closed" as the Haskell sum type hierarchy is. Basically pattern matching on one of the sum-types generated by applying this pattern is the equivalent of isinstancing and downcasting in the OO version. Only because the sum types are exhaustively known to the compiler (only possible because they're closed), if I do add a Circle case to Shape the compiler is able to tell me about all the places that I need to revisit to handle that case.2
If you have a hierarchy that doesn't need a lot of downcasting, it means that the various base classes have substantial and useful interfaces that they guarantee to be available, and you usually use things through that interface rather than switching on what it could possibly be, then you can probably use type classes. You still need all the "leaf" data types (the product types with the actual data fields), only instead of adding sum type wrappers to group them up you add type classes for the common interface. If you can use this style of translation, then you can add new cases more easily (just add the new Circle data type, and an instance to say how it implements the Shape type class; all the places that are polymorphic in any type in the Shape class will now handle Circles as well). But if you're doing that in OO you always have downcasts available as an escape hatch when it turns out you can't handle shapes generically; with this design in Haskell it's impossible.3
But my "real" answer to "how do I represent OO type hierarchies in Haskell" is unfortunately the trite one: I don't. I design differently in Haskell than I do in OO languages4, and in practice it's just not a huge problem. But to say how I'd design this case differently, I'd have to know more about what you're using them for. For example you could do something like represent a shape as a Point -> Bool function (that tells you whether any given point is inside the shape), and having things like circle :: Point -> Int -> (Point -> Bool) for generating such functions corresponding to normal shapes; that representation is awesome for forming composite intersection/union shapes without knowing anything about them (intersect shapeA shapeB = \point -> shapeA point && shapeB point), but terrible for calculating things like areas and circumferences.
1 If you have abstract classes with data members, or you have concrete classes that also have further subclasses you can manually push the data members down into the "leaves", factor out the inherited data members into a shared record and make all of the "leaves" contain one of those, split a layer so that you have a product type containing the inherited data members and a sum type (where that sum type then "splits" into the options for the subclasses), stuff like that.
2 If you use catch-all patterns then the warning might not be exhaustive, so it's not always bullet proof, but how bullet proof it is is up to how you code.
3 Unless you opt into runtime type information with a solution like Typeable, but that's not an invisible change; your callers have to opt into it as well.
4 Actually I probably wouldn't design a hierarchy like this even in OO languages. I find it doesn't turn out to be as useful as you'd think in real programs, hence the "favour composition over inheritance" advice.
You may be looking for a Haskell equivalent of dynamic dispatch, such that you could store a heterogeneous list of values supporting distinct implementations of a common Shape interface.
Haskell's existential types support this kind of usage. It's fairly rare for a Haskell program to actually need existential types -- as Ben's answer demonstrates, sum types can handle this kind of problem. However, existential types are appropriate for a large, open-ended collection of cases:
{-# LANGUAGE ExistentialQuantification #-}
...
class Shape a where
bounds :: a -> AABB
draw :: a -> IO ()
data AnyShape = forall a. Shape a => AnyShape a
This lets you declare instances in an open-ended style:
data Line = Line Point Point
instance Shape Line where ...
data Circle= Circle {center :: Point, radius :: Double}
instance Shape Circle where ...
...
Then, you can build your heterogeneous list:
shapes = [AnyShape(Line a b),
AnyShape(Circle a 3.0),
AnyShape(Circle b 1.8)]
and use it in a uniform way:
drawIn box xs = sequence_ [draw s | AnyShape s <- xs, bounds s `hits` box]
Note that you need to unwrap your AnyShape in order to use the class Shape interface functions. Also note that you must use the class functions to access your heterogeneous data -- there is no other way to "downcast" the unwrapped existential value s! Its type only makes sense within the local scope, so the compiler will not let it escape.
If you are trying to use existential types, yet find yourself needing to "downcast" them, sum types might be a better fit.

Difference between modules and existentials

It's folk knowledge that OCaml modules are "just" existential types. That there's some kind of parity between
module X = struct type t val x : t end
and
data 'a spec = { x : 'a }
data x = X : 'a spec
and this isn't untrue exactly.
But as I just evidenced, OCaml has both modules and existential types. My question is:
How do they differ?
Is there anything which can be implemented in one but not the other?
When would you use one over the other (in particular comparing first-class modules with existential types)?
Completing gsg's answer on your third point.
There are two kinds of way to use modules:
As a structuring construct, when you declare toplevel modules. In that case you are not really manipulating existential variables. When encoding the module system in system-F, you would effectively represent the abstract types by existential variables, but morally, it is closer to a fresh singleton type.
As a value, when using first class modules. In that case you are clearly manipulating existential types.
The other representations of existential types are through GADT's and with objects. (It is also possible to encode existential as the negation of universal with records, but its usage are completely replaced by first class modules).
Choosing between those 3 cases depend a bit in the context.
If you want to provide a lot of functions for your type, you will prefer modules or objects. If only a few, you may find the syntax for modules or objects too heavywheight and prefer GADT. GADT's can also reveal a the structure of your type, for instance:
type _ ty =
| List : ty -> ty list
| Int : int list
type exist = E : 'a ty * 'a -> exist
If you are in that kind of case, you do not need to propagate the function working on that type, so you will end up with something a lot lighter with GADT's existentials. With modules this would look like
module type Exist = sig
type t
val t : t ty
end
module Int_list : Exist = struct
type t = int list
let t = List Int
end
let int_list = (module Int_list:Exist)
And if you need sub-typing or late binding, go for the objects. This can often be encoded with modules but this tend to be tedious.
It's specifically abstract types that have existential type. Modules without abstract types can be explained without existentials, I think.
Modules have features other than abstract types: they act as namespaces, they are structurally typed, they support operations like include and module type of, they allow private types, etc.
A notable difference is that functors allow ranging over types of any (fixed) arity, which is not possible with type variables because OCaml lacks higher kinded types:
module type M = sig
type 'a t
val x : 'a t
end
I'm not quite sure how to answer your last question. Modules and existentials are different enough in practice that the question of when to substitute one for the other hasn't come up.

OO classes represented in Haskell

H!
Suppose I have an abstract class Robot with a few attributs like 'position' and 'weight'. This abstract class implements three methods : 'take', 'drop' and 'move', but also has an abstract method 'makeAction'. Then I have two subclasses of 'Robot' named 'TRobot' and 'FRobot'. Basically, TRobot and FRobot will implement 'makeAction' method, but won't reimplement 'take', 'drop' and 'move'.
My question is how do you do this in Haskell.
I started with the datatype :
data Robot = Robot {position :: Char, weight :: Int}
EDIT :
But I want the functions 'take', 'move' and 'drop' (take :: Box -> Robot -> Robot) to behave the same if Robot is TRobot or FRobot.
However, the function
makeAction :: Robot -> Action
should have a different implementation whether Robot is TRobot or FRobot.
Thanks for the help.
I have <insert object-oriented design here>. How do I implement this in a non-OO language such as Haskell?
Erm... that's not a very good idea. It would probably be far better to take a step back and explain what you are actually trying to achieve. Haskell requires a radically different way of thinking about software design. Without knowing what you actually want to do, it's difficult to say exactly what the best way to achieve it would be.
In particular: How are the two sorts of robot actually different? How are they similar?
It might be that you just want to constructors for a single datatype (as you have written), with a move function that behaves differently for each, and take / drop functions that don't care. Or maybe you want just one constructor, with a field that says which robot type it is. Or maybe you actually want two completely separate types. Without knowing more about what you're trying to do, it's hard to say.
There are a lot of ways to do this in Haskell, but you must first understand that traditional "classes" do not directly correspond to any Haskell construct.
You can do something like this very easily:
data Robot = Robot {
position :: Char,
weight :: Int,
makeAction :: Robot -> String
}
Here, makeAction is a field containing a different function for TRobot and FRobot types. There are a lot of more sophisticated ways to do thus, such as type classes, GADTs, composition, et cetera, but this will get you started.
Type class version
This is a different way to do it, which is more complicated.
Here is a plain type class version:
-- The robot type is expressed using a type parameter (called "a")
data Robot a = Robot {
position :: Char,
weight :: Int
}
-- All robot types have their own "makeAction" function
class RobotType a where
makeAction :: Robot a -> String
data TRobot
instance RobotType TRobot where
makeAction robot = ...
data FRobot
instance RobotType FRobot where
makeAction robot = …
Note that Robot TRobot and Robot FRobot are different types, so if you need a generic robot type, you have to do it with existential types:
data AnyRobot = forall a. RobotType a => AnyRobot (Robot a)
Basically, because we are storing the difference between TRobot and FRobot in the type system, we need existential types to allow us to access those differences at runtime (since types are deleted at compile time).
Haskell doesn't have subtypes in the same way that OO languages do. Usually, if you need that kind of subtype polymorphism you use data types that have higher order functions as fields. It doesn't look like that's necessary in this case though.
Here is how I would approach it:
data RobotType = TRobot | FRobot
data Robot = Robot {
robotType :: RobotType
,position :: Char
,weight :: Int
}
As the others have said, subclassing from imperative OO languages doesn't translate directly to Haskell. One particular way to define your specific example is this:
data Robot = Robot {
position :: Char,
weight :: Int
makeAction :: -- Your function signature
}
makeTRobot :: Char -> Int -> Robot
makeTRobot p w = Robot { position = p,
weight = w,
makeAction = -- TRobot's action function }
You're trying to do subtype polymorphism, which is the primary way to do this kind of thing in OO languages, but Haskell doesn't support this.
Instead, Haskell accomplishes mostly the same thing using typeclass polymorphism. If you've never heard of this before, I'd suggest you read either this or this.
Since I actually want to answer your question, the way you'd get the behavior you are looking for is like this:
Create a typeclass that is similar to an abstract class that defines the minimum behavior that a type has to implement to be included in this typeclass:
class Robot r where
position :: r -> Char
weight :: r -> Int
makeAction :: -- ?? You didn't say.
Then make each of your types an instance of that typeclass:
data FRobot = FRobot { frPosition :: Char, frWeight :: Int }
instance Robot FRobot where
position = frPosition
weight = frWeight
makeAction = -- Whatever you wanted.
Then do the same thing for your other types. After you've done this, you can use Robot r => r -> ... in your type signatures and have r be any kind of Robot. If you don't want to implement a certain method, you can define it as error or undefined but be warned that this is unsafe and undesirable behavior.
EDIT: If you want makeAction to have different types for different Robots... You'll probably be reduced to repetition as there's no way to easily fit that in to the type system otherwise. If you give us a bit more info, I might be able to suggest something more specific.

How to implement this OOP case in Haskell?

In the project I have several different types, defined in different modules and each of them has related functions (the functions have the same name and very similar meaning, so the following make sense). Now I want to create a list, in which it will be possible to have instances of all these types (simultaneously). The only possibility I can think of is something like this:
data Common = A{...} | B{...} | ...
but it implies keeping the definition in a single place, and not in different modules (for A, B, ...). Is there a better way to do this?
UPD
I'm rather new to haskell and write some programs related to my studying. In this case I have different FormalLanguage definition methods: FiniteAutomata, Grammars and so on. Each of them has common functions (isAccepted, representation, ...), so it seemed logical to have a list where elements can be of any of these types.
You are bringing an OOP mindset to Haskell by assuming the correct solution is to store distinct types in a list. I'll begin by examining that asssumption.
Usually we store distinct types in a homogeneous list because they support a common interface. Why not just factor out the common interface and store THAT in the list?
Unfortunately, your question does not describe what that common interface is, so I will just introduce a few common examples as demonstrations.
The first example would be a bunch of values, x, y, and z, that all support the Show function, which has the signature:
(Show a) => a -> String
Instead of storing the type we want to show later on, we could instead just call show directly on the values and store the resulting strings in the list:
list = [show x, show y, show z] :: String
There's no penalty for calling show prematurely because Haskell is a lazy language and won't actually evaluate the shows until we actually need the string.
Or perhaps the type supports multiple methods, such as:
class Contrived m where
f1 :: m -> String -> Int
f2 :: m -> Double
We can transform classes of the above form into equivalent dictionaries that contain the result of partially applying the methods to our values:
data ContrivedDict = ContrivedDict {
f1' :: String -> Int,
f2' :: Double }
... and we can use this dictionary to package any value into the common interface we expect it to support:
buildDict :: (Contrived m) => m -> ContrivedDict
buildDict m = ContrivedDict { f1' = f1 m, f2' = f2 m }
We can then store this common interface itself in the list:
list :: [buildDict x, buildDict y, buildDict z]
Again, instead of storing the distinctly-typed values, we've factored out their common elements for storage in the list.
However, this trick won't always work. The pathological example is any binary operator that expect two operands of equal type, such as the (+) operator from the Num class, which has the following type:
(Num a) => a -> a -> a
As far as I know, there is no good dictionary-based solution for partially applying a binary operation and storing it in such a way that it guarantees it is applied to a second operand of the same type. In this scenario the existential type class is probably the only valid approach. However, I recommend you stick to the dictionary-based approach when possible as it permits more powerful tricks and transformations than the type-class-based approach.
For more on this technique, I recommend you read Luke Palmer's article: Haskell Antipattern: Existential Typeclass.
There are few possibilities:
Possibility 1:
data Common = A AT | B BT | C CT
with AT, BT and CT described in their respective modules
Possibility 2:
{-# LANGUAGE ExistentialQuantification #-}
class CommonClass a where
f1 :: a -> Int
data Common = forall a . CommonClass a => Common a
which is almost the same as OOP superclass, but you cannot do "downcasts". You can then declare implementations for members of common classes in all the modules.
Possibility 3 suggested by #Gabriel Gonzalez:
data Common = Common {
f1 :: Int
}
So your modules implement common interface by using closures to abstract over the 'private' part.
However, Haskell design is usually radically different from OOP design. While it's possible to implement every OOP trick in Haskell, it will be likely non-idiomatic, so as #dflemstr said more information about your problem is welcome.

How to model class hierarchies in Haskell?

I am a C# developer. Coming from OO side of the world, I start with thinking in terms of interfaces, classes and type hierarchies. Because of lack of OO in Haskell, sometimes I find myself stuck and I cannot think of a way to model certain problems with Haskell.
How to model, in Haskell, real world situations involving class hierarchies such as the one shown here: http://www.braindelay.com/danielbray/endangered-object-oriented-programming/isHierarchy-4.gif
First of all: Standard OO design is not going to work nicely in Haskell. You can fight the language and try to make something similar, but it will be an exercise in frustration. So step one is look for Haskell-style solutions to your problem instead of looking for ways to write an OOP-style solution in Haskell.
But that's easier said than done! Where to even start?
So, let's disassemble the gritty details of what OOP does for us, and think about how those might look in Haskell.
Objects: Roughly speaking, an object is the combination of some data with methods operating on that data. In Haskell, data is normally structured using algebraic data types; methods can be thought of as functions taking the object's data as an initial, implicit argument.
Encapsulation: However, the ability to inspect an object's data is usually limited to its own methods. In Haskell, there are various ways to hide a piece of data, two examples are:
Define the data type in a separate module that doesn't export the type's constructors. Only functions in that module can inspect or create values of that type. This is somewhat comparable to protected or internal members.
Use partial application. Consider the function map with its arguments flipped. If you apply it to a list of Ints, you'll get a function of type (Int -> b) -> [b]. The list you gave it is still "there", in a sense, but nothing else can use it except through the function. This is comparable to private members, and the original function that's being partially applied is comparable to an OOP-style constructor.
"Ad-hoc" polymorphism: Often, in OO programming we only care that something implements a method; when we call it, the specific method called is determined based on the actual type. Haskell provides type classes for compile-time function overloading, which are in many ways more flexible than what's found in OOP languages.
Code reuse: Honestly, my opinion is that code reuse via inheritance was and is a mistake. Mix-ins as found in something like Ruby strike me as a better OO solution. At any rate, in any functional language, the standard approach is to factor out common behavior using higher-order functions, then specialize the general-purpose form. A classic example here are fold functions, which generalize almost all iterative loops, list transformations, and linearly recursive functions.
Interfaces: Depending on how you're using an interface, there are different options:
To decouple implementation: Polymorphic functions with type class constraints are what you want here. For example, the function sort has type (Ord a) => [a] -> [a]; it's completely decoupled from the details of the type you give it other than it must be a list of some type implementing Ord.
Working with multiple types with a shared interface: For this you need either a language extension for existential types, or to keep it simple, use some variation on partial application as above--instead of values and functions you can apply to them, apply the functions ahead of time and work with the results.
Subtyping, a.k.a. the "is-a" relationship: This is where you're mostly out of luck. But--speaking from experience, having been a professional C# developer for years--cases where you really need subtyping aren't terribly common. Instead, think about the above, and what behavior you're trying to capture with the subtyping relationship.
You might also find this blog post helpful; it gives a quick summary of what you'd use in Haskell to solve the same problems that some standard Design Patterns are often used for in OOP.
As a final addendum, as a C# programmer, you might find it interesting to research the connections between it and Haskell. Quite a few people responsible for C# are also Haskell programmers, and some recent additions to C# were heavily influenced by Haskell. Most notable is probably the monadic structure underlying LINQ, with IEnumerable being essentially the list monad.
Let's assume the following operations: Humans can speak, Dogs can bark, and all members of a species can mate with members of the same species if they have opposite gender. I would define this in haskell like this:
data Gender = Male | Female deriving Eq
class Species s where
gender :: s -> Gender
-- Returns true if s1 and s2 can conceive offspring
matable :: Species a => a -> a -> Bool
matable s1 s2 = gender s1 /= gender s2
data Human = Man | Woman
data Canine = Dog | Bitch
instance Species Human where
gender Man = Male
gender Woman = Female
instance Species Canine where
gender Dog = Male
gender Bitch = Female
bark Dog = "woof"
bark Bitch = "wow"
speak Man s = "The man says " ++ s
speak Woman s = "The woman says " ++ s
Now the operation matable has type Species s => s -> s -> Bool, bark has type Canine -> String and speak has type Human -> String -> String.
I don't know whether this helps, but given the rather abstract nature of the question, that's the best I could come up with.
Edit: In response to Daniel's comment:
A simple hierarchy for collections could look like this (ignoring already existing classes like Foldable and Functor):
class Foldable f where
fold :: (a -> b -> a) -> a -> f b -> a
class Foldable m => Collection m where
cmap :: (a -> b) -> m a -> m b
cfilter :: (a -> Bool) -> m a -> m a
class Indexable i where
atIndex :: i a -> Int -> a
instance Foldable [] where
fold = foldl
instance Collection [] where
cmap = map
cfilter = filter
instance Indexable [] where
atIndex = (!!)
sumOfEvenElements :: (Integral a, Collection c) => c a -> a
sumOfEvenElements c = fold (+) 0 (cfilter even c)
Now sumOfEvenElements takes any kind of collection of integrals and returns the sum of all even elements of that collection.
Instead of classes and objects, Haskell uses abstract data types. These are really two compatible views on the problem of organizing ways of constructing and observing information. The best help I know of on this subject is William Cook's essay Object-Oriented Programming Versus Abstract Data Types. He has some very clear explanations to the effect that
In a class-based system, code is organized around different ways of constructing abstractions. Generally each different way of constructing an abstraction is assigned its own class. The methods know how to observe properties of that construction only.
In an ADT-based system (like Haskell), code is organized around different ways of observing abstractions. Generally each different way of observing an abstraction is assigned its own function. The function knows all the ways the abstraction could be constructed, and it knows how to observe a single property, but of any construction.
Cook's paper will show you a nice matrix layout of abstractions and teach you how to organize any class as an ADY or vice versa.
Class hierarchies involve one more element: the reuse of implementations through inheritance. In Haskell, such reuse is achieved through first-class functions instead: a function in a Primate abstraction is a value and an implementation of the Human abstraction can reuse any functions of the Primate abstraction, can wrap them to modify their results, and so on.
There is not an exact fit between design with class hierarchies and design with abstract data types. If you try to transliterate from one to the other, you will wind up with something awkward and not idiomatic—kind of like a FORTRAN program written in Java.
But if you understand the principles of class hierarchies and the principles of abstract data types, you can take a solution to a problem in one style and craft a reasonably idiomatic solution to the same problem in the other style. It does take practice.
Addendum: It's also possible to use Haskell's type-class system to try to emulate class hierarchies, but that's a different kettle of fish. Type classes are similar enough to ordinary classes that a number of standard examples work, but they are different enough that there can also be some very big surprises and misfits. While type classes are an invaluable tool for a Haskell programmer, I would recommend that anyone learning Haskell learn to design programs using abstract data types.
Haskell is my favorite language, is a pure functional language.
It does not have side effects, there is no assignment.
If you find to hard the transition to this language, maybe F# is a better place to start with functional programming. F# is not pure.
Objects encapsulate states, there is a way to achieve this in Haskell, but this is one of the issues that takes more time to learn because you must learn some category theory concepts to deeply understand monads. There is syntactic sugar that lets you see monads like non destructive assignment, but in my opinion it is better to spend more time understanding the basis of category theory (the notion of category) to get a better understanding.
Before trying to program in OO style in Haskell, you should ask yourself if you really use the object oriented style in C#, many programmers use OO languages, but their programs are written in the structured style.
The data declaration allows you to define data structures combining products (equivalent to structure in C language) and unions (equivalent to union in C), the deriving part o the declaration allows to inherit default methods.
A data type (data structure) belongs to a class if has an implementation of the set of methods in the class.
For example, if you can define a show :: a -> String method for your data type, then it belong to the class Show, you can define your data type as an instance of the Show class.
This is different of the use of class in some OO languages where it is used as a way to define structures + methods.
A data type is abstract if it is independent of it's implementation. You create, mutate, and destroy the object by an abstract interface, you do not need to know how it is implemented.
Abstraction is supported in Haskell, it is very easy to declare.
For example this code from the Haskell site:
data Tree a = Nil
| Node { left :: Tree a,
value :: a,
right :: Tree a }
declares the selectors left, value, right.
the constructors may be defined as follows if you want to add them to the export list in the module declaration:
node = Node
nil = Nil
Modules are build in a similar way as in Modula. Here is another example from the same site:
module Stack (Stack, empty, isEmpty, push, top, pop) where
empty :: Stack a
isEmpty :: Stack a -> Bool
push :: a -> Stack a -> Stack a
top :: Stack a -> a
pop :: Stack a -> (a,Stack a)
newtype Stack a = StackImpl [a] -- opaque!
empty = StackImpl []
isEmpty (StackImpl s) = null s
push x (StackImpl s) = StackImpl (x:s)
top (StackImpl s) = head s
pop (StackImpl (s:ss)) = (s,StackImpl ss)
There is more to say about this subject, I hope this comment helps!