2016-07-27 9 views
18

ich kann, und wenn ja, wie schreibe ich, die Art Signatur für eine Funktion:Art Signatur für Funktion mit möglicherweise polymorphen Argumenten

g f x y = (f x, f y) 

Derart, daß gegeben:

f1 :: a -> [a] 
f1 x = [x] 

x1 :: Int 
x1 = 42 

c1 :: Char 
c1 = 'c' 

f2 :: Int -> Int 
f2 x = 3 * x 

x2 :: Int 
x2 = 5 

Derart, dass :

g f1 x1 c1 == ([42], ['c']) :: ([Int], [Char]) 
g f2 x1 x2 == (126, 15) :: (Int, Int) 
+1

als @ErikR zeigte Ihnen in seiner (jetzt gelöscht) Antwort die linke Seite ist machbar, aber der Typ auf der rhs hängt von der Art von 'f' so denke ich, dass Sie Typfamilien auch verwenden müssten - wenn das für Sie in Ordnung ist, dann sollte das natürlich nur eine Vermutung sein - denn alles, was ich weiß, gibt es eine Möglichkeit, dies auszudrücken;) – Carsten

+0

Gerne GHC-Erweiterungen verwenden – Clinton

+0

@ user3237465: auf einen Blick (zumindest deine einfache Version) Ich denke, du würdest mit 'f2' in Schwierigkeiten kommen, weil das nicht * forall a * ist, sondern nur für' Int' mehr – Carsten

Antwort

15

Nein, können Sie nicht. Das grundlegende Problem ist, dass Haskells Syntax Sie dazu verleitet hat zu denken, dass die Typen f1 und f2 ähnlicher sind, als sie wirklich sind. Einmal übersetzt in GHC Kern, sie sehen eher anders aus:

f1 :: forall a . a -> [a] 
f2 :: Int -> Int 

Nicht nur das, sondern die entsprechenden Begriffe Blick etwas anders:

f1 = Λa -> λ(x :: a) -> [x] 
f2 = λ(x :: Int) -> 3 * x 

Wie Sie sehen können, f1 und f2 tatsächlich haben verschiedene Anzahlen von Argumenten, wobei f1 einen Typ und einen Wert unddauertbraucht nur einen Wert.

In gewöhnlicheren Umständen, wenn Sie f2 in einen Kontext eine Funktion vom Typ plop erwarten, sagen wir, Int -> [Int], GHC f2 auf die notwendige Art gelten für Sie (dh f2 auf einen bestimmten Typ instanziiert), und alle werden gut sein. Zum Beispiel, wenn Sie

g :: (Int -> [Int]) -> Bool 

haben, und Sie gelten g-f wird GHC kompilieren eigentlich, dass zu

g (f @Int) 

Aber hier wollen Sie Polymorphismus darüber, ob das Instanziierung geschieht oder nicht, die GHC doesn‘ t Unterstützung (ich denke, es wäre eine ziemlich radikale und destruktive Änderung der Kernsprache).

Da Klasseninstanzen, Familienmuster und Typ Familienergebnisse nicht quantifizierbar sind, bin ich ziemlich zuversichtlich, dass es überhaupt keine Möglichkeit gibt zu bekommen, was Sie wollen.

7

Dies ist tatsächlich möglich, wenn es Ihnen nichts ausmacht, ein Proxy Argument hinzuzufügen, mit einer Variation my answer to a similar question here.

Die meisten meiner Erklärung von dieser Antwort hält hier, aber wir müssen auf sie durch Zugabe von ein paar mehr Helfer-Typ-Klassen leicht erweitern (was ich rufe List und Both hier):

{-# LANGUAGE RankNTypes   #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeFamilies   #-} 
{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE ConstraintKinds  #-} 

import   Data.Proxy 

f1 :: a -> [a] 
f1 x = [x] 

x1 :: Int 
x1 = 42 

c1 :: Char 
c1 = 'c' 

f2 :: Int -> Int 
f2 x = 3 * x 

x2 :: Int 
x2 = 5 

class b ~ [a] => List a b 
instance List a [a] 

class (a ~ b, b ~ c) => Both a b c 
instance Both a a a 

g :: (c a r1, c b r2) => 
     Proxy c -> (forall x r. c x r => x -> r) -> a -> b -> (r1, r2) 
g _ f x y = (f x, f y) 

Diese ermöglicht es uns,

ghci> g (Proxy :: Proxy List) f1 x1 c1 
([42],"c") 
ghci> g (Proxy :: Proxy (Both Int)) f2 x1 x2 
(126,15) 

List und Both sind nicht die besten Namen (vor allem List) zu tun, so dass Sie, wenn Sie mit besseren kommen möchten d o Verwenden Sie dies (obwohl ich nicht sicher bin, würde ich vorschlagen, diese Art von Trick im Produktionscode sowieso zu tun, es sei denn, Sie haben einen guten Grund wirklich).