Ho una funzione il cui compito è quello di calcolare un valore ottimale di tipo a
WRT qualche funzione valore di tipo a -> v
tipo esistenziale in funzione di ordine superiore
type OptiF a v = (a -> v) -> a
poi ho un contenitore che vuole conservare tale una funzione insieme ad un'altra funzione che utilizza i valori valori:
data Container a = forall v. (Ord v) => Cons (OptiF a v) (a -> Int)
L'idea è che chi implementa una funzione di tipo OptiF a v
non deve essere disturbato con i dettagli della v
tranne che' s un'istanza di Ord
.
Quindi ho scritto una funzione che accetta una funzione di valore e un contenitore. Utilizzando il OptiF a v
dovrebbe calcolare il valore WRT ottimale val
e collegarlo result
la funzione del contenitore:
optimize :: (forall v. (Ord v) => a -> v) -> Container a -> Int
optimize val (Cons opti result) = result (opti val)
Fin qui tutto bene, ma non posso chiamare optimize
, perché
callOptimize :: Int
callOptimize = optimize val cont
where val = (*3)
opti val' = if val' 1 > val' 0 then 100 else -100
cont = Cons opti (*2)
non lo fa compilare:
Could not deduce (v ~ Int)
from the context (Ord v)
bound by a type expected by the context: Ord v => Int -> v
at bla.hs:12:16-32
`v' is a rigid type variable bound by
a type expected by the context: Ord v => Int -> v at bla.hs:12:16
Expected type: Int
Actual type: Int
Expected type: Int -> v
Actual type: Int -> Int
In the first argument of `optimize', namely `val'
In the expression: optimize val cont
dove la linea 12: 16-32 è optimize val cont
.
In questo caso, ho frainteso i tipi esistenziali? Il forall v
nella dichiarazione di optimize
significa che optimize
si può aspettare da a -> v
qualunque sia lo v
desiderato? O significa che optimize
non può aspettarsi nulla da a -> v
tranne che Ord v
?
Quello che voglio è che il OptiF a v
non è stato risolto per qualsiasi v
, perché voglio inserire alcuni a -> v
in seguito. L'unico vincolo che vorrei imporre è Ord v
. È anche possibile esprimere qualcosa del genere usando tipi esistenziali (o qualsiasi altra cosa)?
Sono riuscito a farlo con un ulteriore typeclass che fornisce una funzione optimize
con una firma simile a OptiF a v
, ma per me è molto più brutto che utilizzare le funzioni di ordine superiore.
Hai fatto la mia giornata e probabilmente le prossime :) Cosa intendi per "esistenziali obsoleti"? Che sono sussunti da GADT come detto in http://en.wikibooks.org/wiki/Haskell/GADT#Existential_types? Ma non dovrei sostituire gli ADT dei GADT dove non sono necessari, giusto? – chs
Per i costruttori semplici (ma probabilmente molti di quelli diversi) la vecchia sintassi 'data' è discutibilmente più leggibile, quindi: no, non dovresti sostituire quelli con GADT (non c'è niente di male, però!). Per tutto ciò che riguarda le variabili di tipo non menzionate nell'header dei dati, utilizzerei la sintassi GADT. – leftaroundabout