How do Frege classes work?
Asked Answered
R

1

7

It seems that Frege's ideas about type-classes differ significantly from Haskell. In particular:

  • The syntax appears to be different, for no obvious reason.

  • Function types cannot have class instances. (Seems a rather odd rule...)

  • The language spec says something about implementing superclasses in a subclass instance declaration. (But not if you have diamond inheritance... it won't be an error, but it's not guaranteed to work somehow?)

  • Frege is less fussy about what an instance looks like. (Type aliases are allowed, type variables are not required to be distinct, etc.)

  • Methods can be declared as native, though it is not completely clear what the meaning of this is.

  • It appears that you can write type.method to access a method. Again, no indication as to what this means or why it's useful.

  • Subclass declarations can provide default implementations for superclass methods. (?)

In short, it would be useful if somebody who knows about this stuff could write an explanation of how this stuff works. It's listed in the language spec, but the descriptions are a little bit terse.

(Regarding the syntax: I think Haskell's instance syntax is more logical. "If X is an instance of Y and Z, then it is also an instance of Q in the following way..." Haskell's class syntax has always seemed a bit strange to me. If X implements Eq, that does not imply that it implements Ord, it implies that it could implement Ord if it wants to. I'm not sure what a better symbol would be though...)


Per Ingo's answer:

  • I'm assuming that providing a default implementation for a superclass method only works if you declare your instances "all at once"?

For example, suppose Foo is a superclass of Bar. Suppose each class has three methods (foo1, foo2, foo3, bar1, bar2, bar3), and Bar provides a default implementation for foo1. That should mean that

instance Bar FB where
  foo2 = ...
  foo3 = ...
  bar1 = ...
  bar2 = ...
  bar3 = ...

should work. But would this work:

instance Foo FB where
  foo2 = ...
  foo3 = ...

instance Bar FB where
  bar1 = ...
  bar2 = ...
  bar3 = ...
  • So if I declare a method as native in a class declaration, that just sets the default implementation for that method?

So if I do something like

class Foobar f where
  foo :: f -> Int
  native foo

  bar :: f -> String
  native bar

then that just means that if I write an empty instance declaration for some Java native class, then foo maps to object.foo() in Java?

In particular, if a class method is declared as native, I can still provide some other implementation for it if I choose to?

  • Every type [constructor] is a namespace. I get how that would be helpful for the infamous named fields problem. I'm not sure why you'd want to declare other things in the scope of this namespace...
Raney answered 10/5, 2012 at 9:3 Comment(0)
T
6

You seem to have read the language spec very carefully. Great. But, no, type classes/instances do not differ substantially from Haskell 2010. Just a bit, and that bit is notational.

Your points:

ad 1. Yes. The rule is that the constraints, if any, are attached to the type and the class name follows the keyword. But this will change soon in favor of the Haskell syntax when multi param type classes are added to the language.

ad 2. Meanwhile, function types are fully supported. This will be included in the next release. The current release has only support for (a->b), though.

ad 3. Yes. Consider our categoric classes hierarchy Functor -> Applicative -> Monad. You can just write the following instead of 3 separate instances:

instance Monad Foo where
    -- implementation of all methods that are due Monad, Applicative, Functor

ad 4. Yes, currently. There will be changes with multi param type classes, however. The lang spec recommends to stay with the Haskell 2010 rules.

ad 5. You'd need that if you model Java Class Hierarchies with type classes. native function declarations are nothing special for type classes/instances. Because you can have an annotation and a default implementation in a class (just as like in Haskell 2010), you can have this in the form of a native declaration, which gives a) the type and b) the implementation (by referring to a Java method).

ad 6. It's orthogonality. Just as you can write M.foo where M is a module, you can write T.foo when T is a type (constructor), because both are namespaces. In addition, if you have a "record", you may need to write T.f x when Frege cannot infer the type of x.

foo x = x.a + x.b    -- this doesn't work, type of x is unknown
-- remedy 1: provide a type signature
foo :: Record -> Int  -- Record being some data type
-- remedy 2: access the field getter functions directly
foo x = Record.a x + Record.b x

ad 7. Yes, for example, Ord has a default implementation for (==) in terms of compare. Hence you can make an Ord instance of something without implementing (==).

Hope this helps. Generally, it must be said, the lang spec needs a) completion and b) updates. If only the day had 36 hours .....

The syntactic issue is also discussed here: https://groups.google.com/forum/?fromgroups#!topic/frege-programming-language/2mCNWMVg5eY

---- Second part ------------

Your example would not work, because, if you define instance Foo FB then this must hold, irrespective of other instances and subclasses. The default foo1 method in Bar will be used only if no Foo instance exists.

then that just means that if I write an empty instance declaration for some Java native class, then foo maps to object.foo() in Java?

Yes, but it depends on the native declaration, it doesn't have to be an Java instance method of that java class, it could also be a static method or a method of another class, or just a member access, etc.

In particular, if a class method is declared as native, I can still provide some other implementation for it if I choose to?

Sure, just like with any other default class methods. Say a default class method is implemented using pattern guards, that does not mean that you must use pattern guards for your implementation.

Look,

native [pure] foo "javaspec" :: a -> b -> c

just means: please make me a frege function foo with type a -> b -> c that happens to use javaspec for implementation. (How exactly is supposed to be described in Chapter 6 of the language reference. It's not done yet. Sorry.) For example:

native pure int2long "(long)" :: Int -> Long

The compiler will see tat this is syntactically a cast operation, and when it sees:

 ... int2long val ... 

it will generate java code like:

((long)(unbox(val))

Apart from that, it will also make a wrapper, so that you can, for example:

map int2long [1,2,4]

The point is that, if I tell you: there is a function X.Y.z, you're not able to tell whether this is a native or a regular one without looking at the source code. Hence, native is the way to lift Java methods, operators and so forth to the Frege realm. Practically everything that is known as "primOp" in Haskell is just a native function in Frege. For example,

pure native + :: Int -> Int -> Int

(It's not always that easy, of course.)

Every type [constructor] is a namespace. I get how that would be helpful for the infamous named fields problem. I'm not sure why you'd want to declare other things in the scope of this namespace...

It gives you somewhat more control regarding the top namespace. Apart from that, you don't have to define other things there. I just did not see a reason to forbid it once I committed to this simple approach to tackle the record field problem.

Tricotine answered 10/5, 2012 at 10:29 Comment(4)
There's a small handful of points I'd like to clarify. I don't think they will fit in a comment though. So I'm going to edit the question instead...Raney
@Raney - sure, no problems.Tricotine
So where do I go to help finish writing the spec? ;-)Raney
@Raney - I am not sure I can parse your question (remember, non-native english speaker) but if you're asking if you can contribute, you're welcome. Just introduce yourself in the discussion forum and tell people there who you are and what you think you can do.Tricotine

© 2022 - 2024 — McMap. All rights reserved.