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
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With