2013-11-01 3 views
7

Ich bin neu bei Haskell und würde gerne wissen, ob es möglich ist, eine Funktion zu definieren, die nur für eine Teilmenge eines bereits existierenden Typs definiert ist, ohne einen neuen Typ definieren zu müssen.Kann eine Funktion für eine Teilmenge eines vorhandenen Typs definiert werden?

Beispiel: Ich möchte eine Funktion erstellen, die nur gerade ganze Zahlen (oder sogar natürliche Zahlen usw.) akzeptiert und zurückgibt, z. diese Zahl quadriert, wie:

Die oben genannten zwei Zeilen funktionieren natürlich nicht.

Ich weiß, dass ich es so schreiben könnte:

squared' :: Integer -> Integer 
squared' n 
    | (even n) = n*n 
    | otherwise = error "n is not even!" 

oder etwas ähnliches, aber ich möchte wissen, ob so etwas wie das nicht-funktionierendes Beispiel ist möglich, wie gut.

Ich hoffe, dass diese Frage nicht ganz dumm ist (oder war bereits beantwortet), aber ich weiß es wirklich nicht viel Haskell noch (so Suche nach einer Antwort wurde Art als auch von schwer) ...

+0

es nicht einfach in Haskell – viorior

Antwort

12

Im Allgemeinen nein. So etwas nennt man einen Subset-Typ, es ist ein Kennzeichen abhängiger Typen, die Haskell nicht hat. Normalerweise wird es implementiert, indem ein Wert mit einem Beweis, dass der Wert eine Eigenschaft erfüllt, verpackt wird, aber da wir keine Beweise in Haskell kennen, stecken wir fest.

Normalerweise ist der Weg, es mit "intelligenten Konstruktoren" zu fälschen.

newtype Even = Even {unEven :: Integer} deriving (Eq, Show, Ord) 

toEven :: Integer -> Maybe Even 
toEven a | even a = Just $ Even a 
     | otherwise = Nothing 

Und dann verstecken Sie die Even Konstruktor.

Wenn Sie wirklich wollen, können Sie zu einer Sprache wechseln, die mit Haskell interagieren kann, die abhängige Typen hat (Coq und Agda in den Sinn).

+0

Hooray für abhängige Arten ist! Wenn das Konzept der abhängigen Typen etwas verwirrend ist, schau dir dieses Video an: http://vimeo.com/77168227. Es bietet ein schönes Beispiel für die Verwendung der abhängigen Typen, um zusätzliche Informationen wie die Länge von Listen usw. auf Typenebene bereitzustellen. – Tetigi

1

Wickeln Sie die Teilmenge in einem newtype

newtype EvenInteger = EvenInteger { 
    unEvenInteger :: Integer 
} deriving (Show, Eq, Ord, Num) 

mkEvenInteger :: Integer -> Maybe EvenInteger 
mkEvenInteger n = case n % 2 of 
    0 -> Just $ EvenInteger n 
    _ -> Nothing 

squared :: EvenInteger -> EvenInteger 
squared n = n * n 
+2

Das geht nicht, '1 :: EvenInteger' spritzt 1 in den Bereich der geraden Zahlen – jozefg

+0

Erg ... du hast Recht. Wenn man bereit ist, sich mit syntaktischer Hässlichkeit zu befassen, könnte diese Lösung funktionieren, ohne "Num" abzuleiten.Dies wäre ein Fall, in dem ich sagen würde, dass die Heilung schlimmer ist als die Krankheit :( –

1

Eine Möglichkeit

newtype Even n = Even n 
getEven (Even n) = 2*n 

squared :: Num n => Even n -> Even n 
squared (Even n) = Even (2*n*n) 
1

wäre Wie an anderer Stelle erwähnt, wie Verfeinerung Typen in LiquidHaskell kann dies auszudrücken. Hier ist, wie es aussieht:

module Evens where 

{[email protected] type Even = {v:Int | v mod 2 = 0} @-} 

{[email protected] square :: Even -> Int @-} 
square :: Int -> Int 
square n = n * n 

-- calling the function: 
yup = square 4 
-- nope = square 3 -- will not compile if this is uncommented 

Sie können dies ausprobieren, indem es hier einstecken: http://goto.ucsd.edu:8090/index.html

Verwandte Themen