2012-11-05 8 views
8

Ho ragione di concludere che non è possibile calcolare maxBound - minBound in Haskell per un tipo arbitrario Enum e Bounded? O mi manca qualche trucco/hack? Questo è quello che ho, che chiaramente non può funzionare:E 'impossibile calcolare la differenza tra il MaxBound e il MinBound di un tipo Enum?

difference :: (Enum a, Bounded a) => Int 
difference = fromEnum maxBound - fromEnum minBound 

Errore:

Foo.hs:37:1: 
    Ambiguous constraint `Enum a' 
     At least one of the forall'd type variables mentioned by the constraint 
     must be reachable from the type after the '=>' 
    In the type signature for `difference': difference :: (Enum a, Bounded a) => Int 

Foo.hs:37:1: 
    Ambiguous constraint `Bounded a' 
     At least one of the forall'd type variables mentioned by the constraint 
     must be reachable from the type after the '=>' 
    In the type signature for `difference': difference :: (Enum a, Bounded a) => Int 

capisco perché sto ricevendo questo errore-non c'è nessun termine effettivo in là con tipo a, così non riesce a capire cosa sia a. La domanda è se c'è un modo per aggirare questo.

+0

ho chiesto sbagliato la prima volta intorno. Ho modificato la domanda. –

+0

come useresti * un valore come questo? – SingleNegationElimination

+0

@SingleNegationElimination Ho un tipo Enum/Bounded e volevo costruire un albero con tutti i possibili valori 'fromEnum' per quel tipo. È conveniente essere in grado di avere una funzione ricorsiva come 'mkTree lower upper' (che funziona comunque, non è così difficile) rompere il problema in intervalli più piccoli di ints. Ma per dare il via alle cose devi essere in grado di scrivere 'mkTree (fromEnum minBound) (fromEnum maxBound)' e ottenere i limiti del tipo giusto. – amalloy

risposta

9

Utilizzare un Proxy per specificare il tipo desiderato e utilizzare ScopedTypeVariables per portare tale tipo in ambito nella definizione della funzione.

{-# LANGUAGE ScopedTypeVariables #-} 

data Proxy a = Proxy 

difference :: forall a . (Enum a, Bounded a) => Proxy a -> Int 
difference Proxy = fromEnum (maxBound :: a) - fromEnum (minBound :: a) 

>>> difference (Proxy :: Proxy Bool) 
1 

Edit: Usando il suggerimento di Daniel:

data Proxy a = Proxy 

difference :: (Enum a, Bounded a) => Proxy a -> Int 
difference p = fromEnum (max' p) - fromEnum (min' p) where 
    max' :: (Bounded a) => Proxy a -> a 
    max' Proxy = maxBound 
    min' :: (Bounded a) => Proxy a -> a 
    min' Proxy = minBound 
+3

Questo può essere fatto anche senza variabili di tipo scoped, se si desidera evitare le estensioni. 'foo :: Bounded a => Proxy a -> (a, a); foo _ = (minBound, maxBound); differenza p = let (min, max) = pippo p in fromEnum max - fromEnum min' –

+0

@DanielWagner Grazie. Mi piace la tua versione migliore. –

+2

Devo dire che mi piace il 'Proxy' con il parametro del tipo di fantasma migliore della risposta di Dave che usa' undefined'. –

12
difference :: (Enum a, Bounded a) => a -> Int 
difference x = fromEnum (maxBound `asTypeOf` x) 
      - fromEnum (minBound `asTypeOf` x) 

Chiamatela come ad esempio difference (undefined :: Char).

Ma si noti che questo traboccherà per alcuni tipi (in particolare Int), così invece utilizzare un Integer risultato:

difference :: (Enum a, Bounded a) => a -> Integer 
difference x = toEnum (fromEnum (maxBound `asTypeOf` x)) 
      - toEnum (fromEnum (minBound `asTypeOf` x)) 
Problemi correlati