2012-03-27 5 views
2

ich eine Funktion haben, die Read a => String -> a geben hat, ist es möglich, eine andere Funktion mit dem gleichen Namen zu haben, die Dinge anders macht, wenn a zum Beispiel ist String? Gibt es GHC-Erweiterungen, die dies ermöglichen?Funktionstyp Spezialisierung in Haskell

Etwas wie:

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

f :: String -> String 
f = id 
+0

Wenn Sie nur über die Leistung besorgt sind, sollten diese Dinge vom Compiler optimiert werden. – leftaroundabout

+0

Ich bin nicht besorgt über die Leistung, ich möchte eine Version von 'lesen', die keine Anführungszeichen um Strings erfordert, aber funktioniert als normal' lesen' für alles andere. –

+0

Die Frage http://stackoverflow.com/questions/9870962/haskell-making-a-superclass-of-num/ adressiert genau das gleiche Problem; einige Antworten dort können nützlich sein. –

Antwort

12

In Haskell, diese Art von Funktion zu überlasten (ad -Hoc-Polymorphismus) wird durch die Verwendung von Typklassen erreicht, nicht durch die Bindung des gleichen Namens unter mehreren Typen. Jetzt

{-# 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. 

, f kann auf jeder Art betrieben werden, für die eine Instanz von F erklärt worden.

Leider kann man nicht mit dem folgenden weg:

instance Read a => F a where f = read 

Vielleicht unintuitively, tut dies nicht eine Instanz von F nur bei den Typen angeben, die eine Instanz von Read haben. Da GHC Instanzen nur mit dem Kopf der Instanzdeklaration auflöst (der Teil rechts von =>), deklariert dies tatsächlich alle Typen a als Instanzen von F, aber es macht einen Typfehler, um f für alles aufzurufen, was nicht auch ist eine Instanz von Read.

Es wird kompiliert, wenn Sie die UndecidableInstances Erweiterung aktivieren, aber das führt nur zu anderen Problemen. Es ist ein Kaninchenloch, in das du dich nicht wirklich wagen willst.

Stattdessen sollten Sie eine Instanz F für jeden einzelnen Typ deklarieren, den Sie mit f bearbeiten möchten. Dies ist nicht sehr belastend für eine einfache Klasse wie diese, aber wenn Sie eine aktuelle Version von GHC verwenden, können Sie die folgende verwenden, um es etwas einfacher zu machen:

{-# LANGUAGE DefaultSignatures #-} 

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

nun für jede Art, die eine ist Instanz von Read, können Sie seine Instanz von F erklären, ohne dass die Durchführung der f explizit bereitzustellen:

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

für alle Arten ohne Instanzen Read, haben Sie immer noch eine explizite Implementierung fürschreiben.

4

Ich habe es zu arbeiten, aber ich hatte auf einer Reihe von fragwürdigen Sprachoptionen aktivieren:

{-# 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) 
+5

'FlexibleInstances' und' TypeSynonymInstances' sind unumstritten; Sie entspannen nur einige Einschränkungen, die durch den Haskell-Standard auferlegt werden, sind aber keine Sorgen."UndecidableInstances" und "OverlappingInstances" sind jedoch ein gefährliches Gebiet und sollten im Allgemeinen vermieden werden, es sei denn, Sie wissen, was Sie tun. – bitbucket