2010-04-28 1 views
6

Quindi sto scrivendo un programma che restituisce una procedura per un determinato problema aritmetico, quindi volevo istanziare un paio di funzioni da mostrare in modo da poter stampare la stessa espressione che valuto quando test. Il guaio è che il codice dato corrisponde (-) alla prima riga quando dovrebbe cadere al secondo.Strange pattern matching con le istanze delle funzioni Show

{-# OPTIONS_GHC -XFlexibleInstances #-} 

instance Show (t -> t-> t) where 
show (+) = "plus" 
show (-) = "minus" 

main = print [(+),(-)] 

rendimenti

[plus,plus] 

Sono solo commettendo un mortale funzioni di stampa il peccato, in primo luogo o c'è qualche modo per farlo per abbinare correttamente?

edit: mi rendo conto che sto ottenendo il seguente avviso:

Warning: Pattern match(es) are overlapped 
     In the definition of `show': show - = ... 

io ancora non so perché si sovrappone, o come fermarlo.

risposta

9

Come sepp2k e MtnViewMark detto, non è possibile pattern match sul valore degli identificatori, solo su costruttori e, in alcuni casi, assegni parità implicite. Pertanto, l'istanza sta vincolando qualsiasi argomento all'identificatore, nel processo che ombreggia la definizione esterna di (+). Sfortunatamente, ciò significa che ciò che stai cercando di fare non funzionerà e non potrà mai funzionare.

Una soluzione tipica a ciò che si desidera eseguire è definire un tipo di dati algebrici di "espressione aritmetica", con un'istanza appropriata show. Si noti che è possibile rendere il proprio tipo di espressione stesso un'istanza di Num, con valori letterali numerici racchiusi in un costruttore "Literal" e operazioni come (+) che restituiscono gli argomenti combinati con un costruttore per l'operazione. Ecco un rapido, ad esempio incompleta:

data Expression a = Literal a 
        | Sum (Expression a) (Expression a) 
        | Product (Expression a) (Expression a) 
        deriving (Eq, Ord, Show) 

instance (Num a) => Num (Expression a) where 
    x + y = Sum x y 
    x * y = Product x y 
    fromInteger x = Literal (fromInteger x) 

evaluate (Literal x) = x 
evaluate (Sum x y) = evaluate x + evaluate y 
evaluate (Product x y) = evaluate x * evaluate y 

integer :: Integer 
integer = (1 + 2) * 3 + 4 

expr :: Expression Integer 
expr = (1 + 2) * 3 + 4 

provarlo in GHCi:

> integer 
13 
> evaluate expr 
13 
> expr 
Sum (Product (Sum (Literal 1) (Literal 2)) (Literal 3)) (Literal 4) 
+0

È fantastico, grazie. (E molto più elegante della soluzione che ho fornito qui sotto.) –

+0

@Sean D: Fa anche una bella illustrazione di dove il tuo mega hack fallire - considera cosa accadrebbe se fosse usato con un operatore sui valori di 'Expression Integer' . 'Sum 6 2' non è uguale a' Literal 8', nonostante il mio codice sia un'istanza perfettamente ragionevole di 'Num'. –

6

Si sovrappone perché tratta lo (+) semplicemente come una variabile, ovvero sul RHS l'identificatore + sarà associato alla funzione che hai chiamato mostra.

Non esiste alcun modo per eseguire la corrispondenza delle funzioni nel modo desiderato.

9

Ecco un modo per pensarci. Considerare:

answer = 42 
magic = 3 

specialName :: Int -> String 
specialName answer = "the answer to the ultimate question" 
specialName magic = "the magic number" 
specialName x = "just plain ol' " ++ show x 

Riesci a capire perché questo non funzionerà? answer nella corrispondenza del modello è una variabile, distinta da answer nell'ambito esterno. Così, invece, dovreste scrivere questo come:

answer = 42 
magic = 3 

specialName :: Int -> String 
specialName x | x == answer = "the answer to the ultimate question" 
specialName x | x == magic = "the magic number" 
specialName x = "just plain ol' " ++ show x 

In realtà, questo è proprio quello che sta succedendo quando si scrive le costanti in un modello. Cioè:

digitName :: Bool -> String 
digitName 0 = "zero" 
digitName 1 = "one" 
digitName _ = "math is hard" 

viene convertito dal compilatore per qualcosa di equivalente a:

digitName :: Bool -> String 
digitName x | x == 0 = "zero" 
digitName x | x == 1 = "one" 
digitName _ = "math is hard" 

Dal momento che si vuole abbinare contro la funzione legata alla (+) piuttosto che legano qualsiasi cosa al simbolo (+), è' d necessario scrivere il codice come:

instance Show (t -> t-> t) where 
show f | f == (+) = "plus" 
show f | f == (-) = "minus" 

Ma, ciò richiederebbe che le funzioni erano paragonabili per eq ualità. E questo è un problema indecidibile in generale.

Si potrebbe obiettare che si sta solo chiedendo al sistema runtime di confrontare i puntatori di funzione, ma a livello di lingua, il programmatore Haskell non ha accesso ai puntatori. In altre parole, non è possibile modificare i riferimenti ai valori in Haskell (*), solo i valori stessi. Questa è la purezza di Haskell e ottiene trasparenza referenziale.

(*) MVar s e altri oggetti simili nella monade IO sono un'altra questione, ma la loro esistenza non invalida il punto.

+0

Tutto diventa chiaro! Ho provato anche a fare (+) e (-) istanze di Eq, ma ovviamente non ha senso neanche (ora). Grazie per l'ottima risposta. –

1

risolto me con un mega hack.

instance (Num t) => Show (t -> t-> t) where 
show op = 
    case (op 6 2) of 
     8 -> "plus" 
     4 -> "minus" 
     12 -> "times" 
     3 -> "divided"