[Haskell-cafe] a simpler way to declare typeclass instances

24 views
Skip to first unread message

Alexey Muranov

unread,
Oct 25, 2014, 11:42:59 AM10/25/14
to Haskell Cafe
Hello,

i am trying to understand how typeclasses work. I know that they can be used as follows (from the manual):

data Foo = Foo {x :: Integer, str :: String}

instance Eq Foo where
(Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)

I am wondering, why is the following seemingly unambiguous syntax not allowed too?

data Foo = Foo { x :: Integer, str :: String }

instance Eq Foo

(Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)

If it was allowed, it seems that it could also be applied to records:

class HasName r where
name :: r -> String

data Bird = Bird { name :: String, wingNumber :: Integer }
data Person = Person { name :: String, likesBirds :: Bool }

instance HasName Bird
instance HasName Person

Alexey.
_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

MigMit

unread,
Oct 25, 2014, 11:51:11 AM10/25/14
to Alexey Muranov, Haskell Cafe
Well, in this case it would look like a definition of a completely independent function (=), which, obviously, clashes with the already defined one.

In general, it's a good idea to keep things that don't make sense without some previous declaration inside the block, established by that declaration.

Adam Gundry

unread,
Oct 25, 2014, 12:25:50 PM10/25/14
to Alexey Muranov, Haskell Cafe
Hi Alexey,

On 25/10/14 16:42, Alexey Muranov wrote:
> i am trying to understand how typeclasses work. I know that they can be used as follows (from the manual):
>
> data Foo = Foo {x :: Integer, str :: String}
>
> instance Eq Foo where
> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)
>
> I am wondering, why is the following seemingly unambiguous syntax not allowed too?
>
> data Foo = Foo { x :: Integer, str :: String }
>
> instance Eq Foo
>
> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)

This case is obviously unambiguous, but what is the general rule? Is the
presence of the Foo data constructor important? What if I wrote the
following, which would be a perfectly good instance method:

foo1 == foo2 = (x foo1 == x foo2) && (str foo1 == str foo2)

In general, should the compiler be doing type inference to determine
which instance a declaration belongs to? In principle, the language
could permit instance methods to be detached from the instances
themselves, but I don't think it should.


> If it was allowed, it seems that it could also be applied to records:
>
> class HasName r where
> name :: r -> String
>
> data Bird = Bird { name :: String, wingNumber :: Integer }
> data Person = Person { name :: String, likesBirds :: Bool }
>
> instance HasName Bird
> instance HasName Person

Note that this currently would only work if you declare Bird and Person
in different modules. I'm not convinced that saving one line of
boilerplate in the instance declarations is worth it.

On the topic of records in particular, you may be interested in the work
on OverloadedRecordFields [1], which might (or might not) appear in GHC
7.10.

Cheers,

Adam

[1] https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields


--
Adam Gundry, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com/

Alexey Muranov

unread,
Oct 25, 2014, 1:38:45 PM10/25/14
to Adam Gundry, Haskell Cafe
Hello Adam,

On 25 oct. 2014, at 18:25, Adam Gundry <ad...@well-typed.com> wrote:

> Hi Alexey,
>
> On 25/10/14 16:42, Alexey Muranov wrote:
>> i am trying to understand how typeclasses work. I know that they can be used as follows (from the manual):
>>
>> data Foo = Foo {x :: Integer, str :: String}
>>
>> instance Eq Foo where
>> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)
>>
>> I am wondering, why is the following seemingly unambiguous syntax not allowed too?
>>
>> data Foo = Foo { x :: Integer, str :: String }
>>
>> instance Eq Foo
>>
>> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)
>
> This case is obviously unambiguous, but what is the general rule? Is the
> presence of the Foo data constructor important? What if I wrote the
> following, which would be a perfectly good instance method:
>
> foo1 == foo2 = (x foo1 == x foo2) && (str foo1 == str foo2)
>
> In general, should the compiler be doing type inference to determine
> which instance a declaration belongs to? In principle, the language
> could permit instance methods to be detached from the instances
> themselves, but I don't think it should.

I have not really thought well about this, but i see no problem with your example: the compiler can use type inference to see that `foo1` and `foo2` are of type `Foo`. I see that type inference is used everywhere in Haskell, back and forth and upside down [1], so why not here? :)

In fact, even the `instance Eq Foo` declaration should be unnecessary IMO because it can be inferred.

I imagine there could be other obstacles of which i haven't thought.

>> If it was allowed, it seems that it could also be applied to records:
>>
>> class HasName r where
>> name :: r -> String
>>
>> data Bird = Bird { name :: String, wingNumber :: Integer }
>> data Person = Person { name :: String, likesBirds :: Bool }
>>
>> instance HasName Bird
>> instance HasName Person
>
> Note that this currently would only work if you declare Bird and Person
> in different modules. I'm not convinced that saving one line of
> boilerplate in the instance declarations is worth it.

It is not to save one line but to avoid namespace pollution with record field names. I do not see why Bird and Person would need to be in different modules if the compiler could determine that they both are instances of HasName. Of course, currently this does not work.

> On the topic of records in particular, you may be interested in the work
> on OverloadedRecordFields [1], which might (or might not) appear in GHC
> 7.10.
>
> Cheers,
>
> Adam
>
> [1] https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields

Thanks for the link, i believe i've read something about it. This looks a bit complicated for me, maybe i'll look at it again later.

Alexey.

[1] http://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function

Alexey Muranov

unread,
Oct 29, 2014, 6:10:36 AM10/29/14
to Adam Gundry, Haskell Cafe
On 25 oct. 2014, at 18:25, Adam Gundry <ad...@well-typed.com> wrote:

> Hi Alexey,
>
> On 25/10/14 16:42, Alexey Muranov wrote:
>> i am trying to understand how typeclasses work. I know that they can be used as follows (from the manual):
>>
>> data Foo = Foo {x :: Integer, str :: String}
>>
>> instance Eq Foo where
>> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)
>>
>> I am wondering, why is the following seemingly unambiguous syntax not allowed too?
>>
>> data Foo = Foo { x :: Integer, str :: String }
>>
>> instance Eq Foo
>>
>> (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)
>
> This case is obviously unambiguous, but what is the general rule? Is the
> presence of the Foo data constructor important? What if I wrote the
> following, which would be a perfectly good instance method:
>
> foo1 == foo2 = (x foo1 == x foo2) && (str foo1 == str foo2)
>
> In general, should the compiler be doing type inference to determine
> which instance a declaration belongs to? In principle, the language
> could permit instance methods to be detached from the instances
> themselves, but I don't think it should.

Maybe to specify the types without type inference, something like this could be used:

(foo1 :: Foo) == (foo2 :: Foo) = (x foo1 == x foo2) && (str foo1 == str foo2)

Alexey.

P.S. Maybe all i say is nonsense, I am not very familiar with the theory of types. I plan to read the Hindley's paper when i have time.
Reply all
Reply to author
Forward
0 new messages