2009-12-01 13 views
7

Ho appena realizzato quanto può essere utile la piccola funzione on.Come rendere Haskell calcolare il tipo polimorfico corretto?

Es:

orderByLength = sortBy (compare `on` length) 

Ma purtroppo, i tipi inferiti può essere un po intuitivo.

Secondo la definizione stessa

f `on` g = \x y -> f (g x) (g y) 

si potrebbe ad esempio sostituire

(==) `on` length 

con

\x y -> (length x) == (length y) 

Ma entrambi hanno diversi tipi!

Il primo ha [a] -> [a] -> Bool mentre il secondo ha il tipo corretto più generico di [a] -> [b] -> Bool.

Questo non consente ovviamente termini corretti come (on (==) length) [1, 2, 3] ["a", "b", "c"] (che dovrebbe produrre True ma ora non riesce nemmeno il controllo dei tipi).

So che questa limitazione si presenta a causa dell'uso di first-rank types, ma come superare questo? Qualcuno può formulare un'implementazione di on che possa gestire correttamente le funzioni polimorfiche (usando quantificazione universale/tipi di rango-n)?

risposta

3
{-# LANGUAGE Rank2Types #-} 
on' :: (a -> a -> b) -> (forall d. c d -> a) -> c e -> c f -> b 
on' f g x y = f (g x) (g y) 

Questo comporta

 
Prelude> :t on' (==) 
on' (==) :: (Eq a) => (forall d. c d -> a) -> c e -> c f -> Bool 
Prelude> :t on' (==) length 
on' (==) length :: [e] -> [f] -> Bool 

D'altra parte, questa firma rende anche flip on' id illegale, che è alquanto meno desiderabile.


{-# LANGUAGE TemplateHaskell #-} 
import Language.Haskell.TH 
onE f g = do 
    x <- newName "x" 
    y <- newName "y" 
    lamE [varP x, varP y] $ f `appE` (g `appE` varE x) `appE` (g `appE` varE y) 
 
Prelude> :set -XTemplateHaskell 
Prelude> $(onE [|(==)|] [|length|]) [1,2,3] ["a","b","c"] 
True 
Prelude> $(onE [|(==)|] [|id|]) 4 5 
False 
+0

cosa Cool - Che cosa è 'C'? Un tipo '* -> *'? Ah, è per avvolgere il potenziale uso di '[tipo]' ... Puoi generalizzarlo per qualsiasi * tipo *? – Dario

+0

Fantastico, grazie per entrambe le idee – Dario

Problemi correlati