Il polimorfismo parametrico in Haskell si basa sul fatto che tutti i valori dei tipi t :: *
sono rappresentati in modo uniforme come puntatore a un oggetto runtime. Pertanto, lo stesso codice macchina funziona per tutte le istanze di valori polimorfici.
Funzioni polimorfiche di contrasto in Rust o C++. Ad esempio, la funzione di identità ha ancora tipo analogico a forall a. a -> a
, ma poiché i valori di diversi tipi a
possono avere dimensioni diverse, i compilatori devono generare codice diverso per ogni instatiazione. Ciò significa anche che non possiamo passare funzioni polimorfiche giro in scatole di esecuzione:
data Id = Id (forall a. a -> a)
poiché tale funzione dovrebbe funzionare correttamente per oggetti arbitrari dimensioni. Richiede qualche infrastruttura aggiuntiva per consentire questa funzionalità, ad esempio potremmo richiedere che una funzione runtime forall a. a -> a
prenda argomenti impliciti aggiuntivi che portano informazioni sulla dimensione e sui costruttori/distruttori dei valori a
.
Ora, il problema con newtype Vec = Vec (# Float#, Float# #)
è che, anche se ha Vec
tipo *
, codice runtime che prevede valori di alcune t :: *
non in grado di gestirlo. È una coppia di float allocata allo stack, non un puntatore a un oggetto Haskell, e passarla al codice che si aspetta oggetti Haskell si tradurrebbe in segfault o errori.
In generale (# a, b #)
non è necessariamente a dimensione di puntatore, quindi non è possibile copiarlo in campi di dati di dimensioni puntatore.
Le famiglie di tipi che restituiscono tipi #
non sono consentite per motivi correlati. Si consideri il seguente:
type family Foo (a :: *) :: # where
Foo Int = Int#
Foo a = (# Int#, Int# #)
data Box = forall (a :: *). Box (Foo a)
nostro Box
non è rappresentabile runtime, poiché Foo a
ha dimensioni diverse per i diversi a
-s. Generalmente, il polimorfismo su #
richiederebbe la generazione di codice diverso per diverse istanze, come in Rust, ma questo interagisce male con il normale polimorfismo parametrico e rende difficile la rappresentazione runtime di valori polimorfici, quindi GHC non si preoccupa di nulla di tutto questo.
(Non dire però che un'implementazione utilizzabile non poteva forse essere concepito)
"problema con' newtype Vec = Vec (# Float #, Float # #) 'è che anche se' Vec' ha un carattere '*' "Ma perché? Perché deve essere gentile '*'? Quando scrivo 'type Vec = (# Float #, Float # #)' Vec ha gentile '#', quindi mi aspetto che newtype abbia anche il carattere '#. –
'type Vec = ...' è solo un sinonimo. 'newtype' definisce, beh, un nuovo tipo che è diverso dal tipo del valore spostato. Succede così che tutti i costrutti Haskell per la definizione di nuovi tipi restituiscono in kind '*'. 'newtype'-s per i tipi unboxed richiederebbe una grossa fetta dell'infrastruttura che ho menzionato sopra per abilitare il polimorfismo su oggetti di dimensioni diverse, altrimenti sarebbe utilizzabile solo in modo monomorfico, il che non li renderebbe particolarmente migliori rispetto al solo uso del tipo avvolto . –
@ AndrásKovács Potrebbe essere di tipo '#', ma ad es. 'id :: a -> a' non può essere specializzato per' a ~ Vec' da 'a :: *'. Questa limitazione è legata al fatto che il runtime gestisce i dati in scatola in modo uniforme (con puntatori) quando si desidera passare dati unboxed non elaborati, quindi è necessario un ID 'personalizzato 'per ogni tipo non inserito. Non molto diverso dall'incapacità di Java di avere generici come 'T' dato che 'int' non è un tipo di riferimento. –
chi