4
votes

I'm a newbie in Haskell.

I'm looking if there's any way to create an instance of type of a class.

Is there any way to get this code working without using data or newtype?

type N = ∀n. (n -> n) -> n -> n

instance Printable N where
        print :: N -> IO ()
        read  :: String -> N 

When I try to load the module in GHCi it tells me:

Illegal polymorphic or qualified type: N
In the instance declaration for ‘Printable N’
1
Why don't you want to use newtype ?ErikR
if I use newtype or data I need to write something like: newtype N = N (n -> n) -> n -> n) and then rewrite functions like (+) (N a) (N b) = stuff instead of (+) = \a b -> stuff without unboxing a or b.CheeseLollipop
Impredicativity is best to be avoided in current GHC. No way around that but to use newtype or data. (Also look up safe coercions, which make newtypes much simpler to handle in some cases).chi

1 Answers

7
votes

What you've written there looks a lot like a class declaration, not an instance. Perhaps you meant this?

class Printable n where
  print :: n -> IO ()
  read  :: String -> n

Note that the n has to be lowercase, because that's the type variable you're quantising the class over. If you actually want to define an instance, then you, well, instantiate n with N:

instance Printable N where
  print n = ...
  read str = ...

At this point the type signatures are all fixed (from the class definition) and what you need to write are the actual bindings of these functions, hence it must be =, not ::.

Question is: why do you need your own class anyway? It's only going to cause name clashes with the standard functions print and read that are already in the prelude. What you actually should be doing is instantiate those standard classes with your N type, i.e.

instance Show N where
  show n = ...
instance Read N where
  readsPrec _ str = ...

That said, to get to the actual question you've asked: no, it is not possible to define any instances sensibly for a polymorphic type like ∀ n . (n->n) -> n->n. How is the compiler supposed to distinguish this from more specific types like (Int->Int) -> Int->Int, or more general ones like ∀ n m . (n->m) -> n->m? It's pretty hopeless. The correct thing to do is to just wrap it in a newtype; that hides the universal quantification and allows the compiler to properly distinguish N from other types.

Alternatively, you can just write monomorphic functions that accept/yield N directly:

showChurch :: N -> String
showChurch n = ...
readsPrecChurch :: Int -> ReadS N
readsPrecChurch _ str = ...

Actually the latter one is already too much for the type system: the ReadS N is short for

readsPrecChurch :: Int -> String -> [(∀ n . (n->n) -> n->n, String)]

universal quantification within a list? Uh oh. That's an impredicative type. GHC does have an -XImpredicativeTypes extension, but it doesn't really work.

Again, just avoid these problems by not openly using polymorphic types. There are a few great uses for Rank-N types (notably lenses), but most of the time they're completely overkill and unnecessary. There's certainly never a good reason to actually use church numerals like that.

newtype N = Church { getChurch :: ∀ n . (n->n) -> n->n }

will allow you to define arbitrary instances or functions without problem. And, practically speaking, simply doing

type N = Int

with all the standard instances that come for integers is of course just as good...