2012-05-23 2 views
7

Der folgende Code soll entweder ein Double oder ein Integer erzeugen. s wird entweder negate oder id angenommen; n der ganze Teil; und f der Bruchteil oder Nothing für eine ganze Zahl.Verlust von Polymorphismus nach Mustererkennung

computeValue :: Num a => (a->a) -> Integer -> (Maybe Double) -> Either Double Integer 
computeValue s n Nothing = Right $ s n 
computeValue s n (Just a) = Left $ s (fromIntegral n + a) 

, wenn ich das kompilieren bekomme ich:

test1.hs:2:28: 
    Couldn't match type `Integer' with `Double' 
    Expected type: Either Double Integer 
     Actual type: Either Double a 
    In the expression: Right $ s n 
    In an equation for `computeValue': 
     computeValue s n Nothing = Right $ s n 

test1.hs:2:38: 
    Couldn't match type `Integer' with `Double' 
    In the first argument of `s', namely `n' 
    In the second argument of `($)', namely `s n' 
    In the expression: Right $ s n 

Es scheint so, irgendwie der Compiler hat den Überblick verloren, die Tatsache, dass s polymorph ist. Was ist hier passiert und wie repariere ich es?

Antwort

10

s ist von innen Ihrer Funktion nicht polymorph: Sie jede Funktion verwenden können, die auf einigeNum Instanz als dieser Parameter arbeitet, könnte es eine Funktion sein, die nur auf Complex funktioniert! Was Sie benötigen, ist eine universell quantifizierte Funktion s, d. H. Eine, die tatsächlich mit beliebigenNum Instanz aufgerufen werden kann.

{-# LANGUAGE Rank2Types #-} 

computeValue :: (forall a . Num a => a->a) -> Integer -> Maybe Double -> Either Double Integer 
computeValue s n Nothing = Right $ s n 
computeValue s n (Just a) = Left $ s (fromIntegral n + a) 

das dann funktioniert:

Prelude Data.Either> computeValue id 3 Nothing 
Right 3 
Prelude Data.Either> computeValue negate 57 (Just pi) 
Left (-60.1415926535898) 
+0

Interessant! Ich hatte gefunden, was falsch war (es wollte wirklich ein "Entweder ein A") als eine Rückkehr, aber ich wusste nicht, dass es einen Weg gab. –

+6

@leftaroundabout: Sie brauchen tatsächlich _universally_ quantifizierten 's' und das ist, was die Rang 2 Signatur tut. 'ExistentialQuantification' tut in diesem Beispiel nichts, die wichtige Erweiterung ist nur' Rank2Types'. – Vitus

+0

@Vitus: Ach, richtig! Ich vermische es weiter, aber das macht viel mehr Sinn, darüber nachzudenken. Bearbeitet. – leftaroundabout