17

sagen habe ich eine Funktion:Referenz Transparenz mit Polymorphismus in Haskell

f :: Int -> (Rational, Integer) 
f b = ((toRational b)+1,(toInteger b)+1) 

ich abstrahieren will weg die (+1) etwa so:

f :: Int -> (Rational, Integer) 
f b = (h (toRational b) 
     ,h (toInteger b)) 
    where h = (+1) 

Das wird nicht funktionieren offensichtlich, aber wenn ich geben sie die Art Signatur wird es funktionieren:

f :: Int -> (Rational, Integer) 
f b = (h (toRational b) 
     ,h (toInteger b)) 
    where h :: Num a => a -> a 
      h = (+1) 

sagen, dass ich jetzt abstrakt die Funktion, indem man h als parame fördern wollen ter:

f :: Num a => Int -> (a -> a) -> (Rational, Integer) 
f b g = (h (toRational b) 
     ,h (toInteger b)) 
    where h :: Num a => a -> a 
      h = g 

Ich erhalte eine Fehlermeldung, dass der innere a nicht gleich ein wie der äußere ist.

Kann jemand diese Funktion richtig schreiben? Ich möchte eine polymorphe Funktion g zu f übergeben und polymorph verwenden.

Ich habe diese Situation schon mehrmals in sehr verschiedenen Projekten erlebt, und ich konnte keine gute Lösung finden.

+1

Willkommen in der Welt von * höherrangigen Typen *! – Ingo

+5

"Das wird offensichtlich nicht funktionieren" Eigentlich ist es nicht offensichtlich, dass das nicht funktionieren wird: '(+1)' ist polymorph, nur die [gefürchtete Monomorphismus-Einschränkung] (http://www.haskell.org/haskellwiki/Monomorphism_restriction) Verhindert, dass "h" dieses Merkmal erbt. Wenn Sie '-XNoMonomorphismRestriction' setzen, funktioniert Ihr zweites Code-Feld gut. – leftaroundabout

Antwort

17

fand ich die Lösung: wie so den forall quantifier mit:

{-# LANGUAGE RankNTypes #-} 
f :: Int -> (forall a. Num a=> a -> a) -> (Rational, Integer) 
f b g = (h (toRational b) 
     ,h (toInteger b)) 
    where h :: Num a => a -> a 
      h = g 

Was natürlich in gedreht werden kann:

f :: Int -> (forall a. Num a=>a -> a) -> (Rational, Integer) 
f b g = (g (toRational b) 
     ,g (toInteger b)) 
+11

Das ist genau richtig. Ohne den Vorfall wird Haskell versuchen, das Funktionsargument innerhalb des Funktionskörpers zu spezialisieren. Dies führt zum unlösbaren System '(Rational ~ a) && (Integer ~ a)'. Die Forall-Bindung besagt, dass Sie eine unspezialisierte Funktion übergeben und sie nur an der Call-Site spezialisieren. Der Nachteil ist, dass Sie keinen anderen "a" -Typ eingeben können und sicherstellen, dass er der Funktion entspricht - nicht, dass Sie dies in dieser Situation tun würden. –