0

I'm attempting to add two polymorphic tuples together in a pairwise manner. (The types of the first element in one tuple should be the same as the first in the second and likewise for the second element) Here's my code:

module Main where

class Coordinate a where

    createCoordinate :: a

    getFirst :: (a,b) -> a

    getSecond :: (a,b) -> b

    addCoordinates :: (a,b) -> (a,b) -> (a,b)

instance Coordinate () where

    createCoordinate = ()

    getFirst (a,b) = a

    getSecond (a,b) = b

    addCoordinates a b = (getFirst a + getFirst b, getSecond a + getSecond b)

So, the problem is with my addCoordinates function. I was wondering if anyone could offer me any help with how to go about implementing the function. Thanks! :)

  • Yeah, I'm very new to Haskell. My previous programming experience would have been with Java. Is there something specific I could clarify for you? – Haskell Sean Nov 26 '16 at 19:42
  • Why do all your class's function work on tuples rather than coordinates? What's the purpose of the `b` type variable that most of your methods have - like why do you work with tuples where the first element is a coordinate and the second element is arbitrary? And how does it make sense for the unit value to be a coordinate? – sepp2k Nov 26 '16 at 19:46
  • I don't think you want a `class` here. You don't want to make arbitrary types behave as coordinates. You want a data type that contains a pair of values. – pat Nov 26 '16 at 19:46
  • Oh and on a more general level: Why have a coordinate class at all (as opposed to just a data type)? – sepp2k Nov 26 '16 at 19:46
  • You probably don't want to create any classes just yet. If you want to add two 2-tuples, try this function: `addCoordinates (x,y) (x',y') = (x+x',y+y')` (remove the class definition and the instance definition you have, this is a standalone function). Use GHCi to examine its type. – n. m. could be an AI Nov 26 '16 at 19:48
  • Well the coordinate is an X and a Y value so I thought I should represent it as a tuple. The b represents the Y part of the coordinate but could be of a different type then that of the a which represents the X value. I don't really understand what you mean by unit value though. – Haskell Sean Nov 26 '16 at 19:54
  • The reason I didn't use a datatype was because I was trying to use this as an exercise for learning about classes and creating instances of them. I agree it would make more sense to use a data type though. – Haskell Sean Nov 26 '16 at 20:01
  • @HaskellSean You wrote `Coordinate a`, so `a` is the type that implements the `Coordinate` class. So `a` is not the first part of your coordinate, it *is* your coordinate. So `(a, b)` is a tuple that contains a coordinate and an arbitrary type. By the "unit value" I mean `()`. That is, the value `()` the only value of the type `()` (also called the unit type). When you write `instance Coordinate ()`, you're saying "the type `()` implements `Coordinate`", which makes the `()` value a coordinate. But that makes no sense. – sepp2k Nov 26 '16 at 23:39
  • 4
    You don't use type classes for modeling data, so you've started with completely the wrong kind of task to use for learning classes. If that's your main goal you'll be **much** better served by giving up and finding a different exercise. Classes in Haskell are not like classes in OO languages, they're much more similar to OO interfaces. You use data types to model data. Classes are for modeling abstract interfaces that many different types might want to implement (each in their own unique way). – Ben Nov 27 '16 at 05:47
  • 3
    You should not start defining your own classes until you have plenty of experience using classes others have defined. Whereas Haskell programmers will often define loads of data types, we tend to be much more restrained about classes. Making one "for practice" that doesn't capture a real abstraction may not be so helpful. Practice using, and writing instances for, `Show`, `Read`, `Ord`, `Functor`, `Applicative`, `Monad`, `Traversable`, `Monoid`, `Category`, etc. Then try to formulate a class that expresses a specific concept involving one or more operations. Try writing a `Ring` class, say. – dfeuer Nov 27 '16 at 05:49
  • @sepp2k Ah ok I understand what you mean now. However, I don't understand how I would go about having what I want for the definition of the class. How I would have an (a,b) and no just an a. – Haskell Sean Nov 27 '16 at 16:13
  • @HaskellSean It is not yet clear to me what you want. If you want coordinates to always be tuples of integers, you don't want a class at all. A class only makes sense if you have more than one instance (in OO terms: there's no need to implement an interface if you only one class to implement it - note that in this analogy OO classes map to Haskell data types and OO interfaces map to Haskell type classes). – sepp2k Nov 27 '16 at 16:20
  • If you want tuples to be one possible instance of Coordinates, the tuples should only appear in the instance definition (which should look something like `instance Coordinate (Integer, Integer) where ...`), not in the class definition. – sepp2k Nov 27 '16 at 16:20
  • @sepp2k I updated my original post to be a bit more specific about what I was trying to accomplish – Haskell Sean Nov 27 '16 at 16:36
  • @HaskellSean There's a big terminology mix up here: In Haskell there are type classes, types and values. A type can be an instance of a type class (or multiple type classes or none) and a value has a type. So a phrase like "add two instances of a class" does not make sense because an instance of a class is a type and you can't add types. Once you understand that, you need to decide which types you want to be instances of your class. – sepp2k Nov 27 '16 at 16:43
  • @sepp2k When I say "add two instances of a class" I mean adding the first value of an instance of a Coordinate with the first value of another instance of a Coordinate (Both of these instances to be added will be of the same type but other Coordinates may differ in type) eg. adding (1,2.1) and (2, 1.3) would return (3,3.4) and adding (2.1,-1) and (1.3,-2) would return (3.4,-3) – Haskell Sean Nov 27 '16 at 16:54
  • @HaskellSean Please read carefully what I wrote about the terminology in Haskell. `(1,2.1)` is not an instance of the Coordinate class and it can't possibly be. Only types can be instances of a type class, values can't. – sepp2k Nov 27 '16 at 17:00

2 Answers2

4

You probably want a data type, not a class:

data Coordinate a b = Coordinate { getFirst :: a, getSecond :: b }
    deriving (Eq, Ord, Show)

Your functions would then become:

createCoordinate :: a -> b -> Coordinate a b
createCoordinate a b = Coordinate a b

addCoordinates :: (Num a, Num b) => Coordinate a b -> Coordinate a b -> Coordinate a b
addCoordinates (Coordinate a1 b1) (Coordinate a2 b2) = Coordinate (a1+a2) (b1+b2)

Note that a and b can be of any type, but addCoordinates only works if they are instances of Num, because we wish to apply + to them. You do not need a typeclass to define Coordinate.

A typeclass would allow you to define things that can be initialized to default values for instance:

class DefaultInitializable a where
    defaultInit :: a

We can then make Int an instance of this class:

instance DefaultInitializable Int where
    defaultInit = 0

And we can make Coordinate an instance as long as its parameters are also instances:

instance (DefaultInitializable a, DefaultInitializable b) => DefaultInitializable (Coordinate a b) where
    defaultInit = Coordinate default default
pat
  • 12,587
  • 1
  • 23
  • 52
  • I was trying to to this as an exercise to learn about classes, I think the above would probably be a good solution though but I don't know if it really helps me learning about polymorphic classes and implementing instances of those classes. Thanks for your help though. – Haskell Sean Nov 26 '16 at 19:59
  • You could create a class for things that can be default initialized, and use it to implement createCoordinate (assuming a and b are default initializable). – pat Nov 26 '16 at 20:02
  • I'm not really sure I understand what you mean by default initialized, could you explain that please? – Haskell Sean Nov 26 '16 at 20:06
  • 1
    Think of classes more like collections of verbs that you would like to apply to arbitrary nouns, and data types as the nouns. A Coordinate is a noun. DefaultInitializable has a verb, `default` which can return a noun – pat Nov 26 '16 at 20:06
  • Why not just bundle the field accessors with the data type? `data Coordinate a b = Coordinate { getFirst :: a, getSecond :: b } deriving (Eq, Ord, Show)`? – Alec Nov 26 '16 at 20:12
  • I tried to use this and got an error. parse error on input "::" which is in relation to `default :: a`. – Haskell Sean Nov 26 '16 at 20:39
  • So for your implementation how would I go about implementing the functions I want? If I want to have a class that defines those different functions. – Haskell Sean Nov 26 '16 at 20:57
  • `default` is a keyword in Haskell. Use a different name. –  Nov 26 '16 at 21:23
  • What functions do you want to add? You only need a class if you want them to apply to arbitrary types. If you're OK with them only applying to Coordinates, then no class is needed. – pat Nov 27 '16 at 05:22
  • I do want to apply them to arbitrary types. The functions i want to implement are: `createCoordinate :: a -> b -> (a,b)` `getFirst :: (a,b) -> a` `getSecond :: (a,b) -> b` `addCoordinates ` Where addCoordinates is a function that will take the first element of an instance of a Coordinate and add it in a pairwise manner to the first element of another instance of a Coordinate. – Haskell Sean Nov 27 '16 at 16:07
  • But you don't want different types to be Coordinates. A Coordinate is always a pair of values of arbitrary types. You have changed the type of `createCoordinate` from your original post. Given that you intend to provide values for `a` and `b`, it no longer needs to be default initializable. – pat Nov 27 '16 at 16:50
  • @pat I was just playing around with things to see what worked and that was what I had at the time. The edited version of my post is what I actually what I just am not sure on how to go about it in haskell. – Haskell Sean Nov 27 '16 at 16:59
0

I feel this might be a solution to what I wanted

module Main where

class Coordinate c where

    createCoordinate :: x -> y -> c x y

    getFirst :: c x y -> x

    getSecond :: c x y -> y

    addCoordinates :: (Num x) => (Num y) => c x y -> c x y -> c x y

instance Coordinate (,) where

    createCoordinate a b = (a,b)

    getFirst (a,_) = a

    getSecond (_,b) = b

    addCoordinates a b = (getFirst a + getFirst b, getSecond a + getSecond b)
  • But the question stands, _what does this class gain you_ over simply using suitable functions on plain monomorphic `(a,b)` tuples? Or indeed a _vector class_ like [`R2`](http://hackage.haskell.org/package/linear-1.20.5/docs/Linear-V2.html#t:R2), even better [`VectorSpace`](http://hackage.haskell.org/package/vector-space-0.10.4/docs/Data-VectorSpace.html#t:VectorSpace), which ensure that the elements are treated in a uniform way. Nevertheless I think the idea might be useful for entirely different applications; it inspired me [to a new question](http://stackoverflow.com/questions/40835094/). – leftaroundabout Nov 27 '16 at 23:32