2

I have a function that has type Read a => String -> a, is it possible to have another function with the same name that does things differently when a is for example String? Are there any GHC extensions that allow this?

Something like:

f :: Read a => String -> a
f = read

f :: String -> String
f = id
Marek Sapota
  • 20,103
  • 3
  • 34
  • 47
  • If you're just concerned about performance, these kind of things should get optimized out by the compiler. – leftaroundabout Mar 27 '12 at 00:31
  • I'm not concerned about performance, I want a version of `read` that doesn't require quotation marks around strings, but works as normal `read` for everything else. – Marek Sapota Mar 27 '12 at 00:37
  • The question http://stackoverflow.com/questions/9870962/haskell-making-a-superclass-of-num/ addresses exactly the same problem; some answers there may be useful. – John L Mar 27 '12 at 01:23

2 Answers2

12

In Haskell, this kind of function overloading (ad-hoc polymorphism) is accomplished by using type classes, not by binding the same name under multiple types.

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}

class F a where f :: String -> a

instance F String where f = id
instance F Int where f = read
instance F Char where f = read
instance F Float where f = read
-- etc.

Now, f can operate on any type for which an instance of F has been declared.

Unfortunately, you can't get away with the following:

instance Read a => F a where f = read

Perhaps unintuitively, this does not declare an instance of F only for types which have an instance of Read. Because GHC resolves instances using only the head of the instance declaration (the part to the right of the =>), this actually declares all types a to be instances of F, but makes it a type error to call f on anything which is not also an instance of Read.

It will compile if you enable the UndecidableInstances extension, but this just leads to other problems. It's a rabbit hole you don't really want to venture down.

Instead, you should declare an instance of F for each individual type you intend f to operate on. This isn't very burdensome for a simple class like this one, but if you use a recent version of GHC, you can use the following to make it slightly easier:

{-# LANGUAGE DefaultSignatures #-}

class F a where f :: String -> a
                default f :: Read a => String -> a
                f = read

Now, for any type which is an instance of Read, you may declare its instance of F without having to provide the implementation of f explicitly:

instance F Int
instance F Char
instance F Float
-- etc.

For any types without instances of Read, you'll still have to write an explicit implementation for f.

bitbucket
  • 1,307
  • 7
  • 11
4

I got it to work, but I had to turn on a bunch of questionable language options:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances #-}

class SpecialRead a where
  specialRead :: String -> a

instance Read a => SpecialRead a where
  specialRead = read

instance SpecialRead String where
  specialRead = id

main = do
    print (specialRead "10" :: Int)
    print (specialRead "10" :: String)
pat
  • 12,587
  • 1
  • 23
  • 52
  • 5
    `FlexibleInstances` and `TypeSynonymInstances` are uncontroversial; they just relax some restrictions imposed by the Haskell standard, but aren't anything to worry about. `UndecidableInstances` and `OverlappingInstances` are dangerous territory, however, and should generally be avoided unless you know what you're doing. – bitbucket Mar 27 '12 at 01:12