functions - syntax record haskell
"DeclaraciĆ³n de instancia ilegal" al declarar la instancia de IsString (2)
¿Por qué se rechaza esto?
Porque:
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
¿Hay algo que me haya perdido?
Sí:
Use -XFlexibleInstances if you want to disable this.)
Estoy escribiendo una aplicación que usa cadenas UTF-16, y para hacer uso de la extensión de cadenas sobrecargadas, intenté crear una instancia de IsString
para ella:
import Data.Word ( Word16 )
import Data.String ( IsString(fromString) )
type String16 = [Word16]
instance IsString [Word16] where
fromString = encodeUTF16
encodeUTF16 :: String -> String16
El problema es que cuando intento compilar el módulo, GHC 7.0.3 se queja:
Data/String16.hs:35:10:
Illegal instance declaration for `IsString [Word16]''
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `IsString [Word16]''
Si comento la declaración de instancia, se compila con éxito.
¿Por qué se rechaza esto? La instancia de [Char]
parece bastante a la misma cosa, pero se compila bien. ¿Hay algo que me haya perdido?
Después de echar un vistazo a los manuales de GHC y al wiki de Haskell (especialmente la página de instancia de la Lista ), tengo una mejor idea de cómo funciona esto. Aquí hay un resumen de lo que he aprendido:
Problema
El Informe Haskell define una declaración de instancia como esta:
El tipo ( T u 1 ... u k ) debe tomar la forma de un constructor de tipo T aplicado a las variables de tipo simple u 1 , ... u k ; además, T no debe ser un sinónimo de tipo , y la ui debe ser distinta.
Las partes resaltadas en negrita son las restricciones que me hicieron tropezar. En inglés, son:
- Cualquier cosa después del constructor de tipo debe ser una variable de tipo.
- No puede usar un alias de tipo (usar la palabra clave de
type
) para sortear la regla 1.
Entonces, ¿cómo se relaciona esto con mi problema?
[Word16]
es solo otra forma de escribir [] Word16
. En otras palabras, []
es el constructor y Word16
es su argumento.
Así que si intentamos escribir:
instance IsString [Word16]
que es lo mismo que
instance IsString ([] Word16) where ...
no funcionará porque viola la regla 1, como el compilador señala amablemente.
Tratando de ocultarlo en un sinónimo de tipo
type String16 = [Word16]
instance IsString String16 where ...
Tampoco funcionará, porque viola la parte 2.
Por lo tanto, es imposible obtener [Word16]
(o una lista de cualquier cosa) para implementar IsString
en el estándar Haskell.
Entra ... (drumroll por favor)
Solución # 1: newtype
La solución que sugirió @ehird es envolverla en un newtype
:
newtype String16 = String16 { unString16 :: [Word16] }
instance IsString String16 where ...
String16
las restricciones porque String16
ya no es un alias, ¡es un tipo nuevo (disculpa el juego de palabras)! El único inconveniente de esto es que luego tenemos que ajustarlo y desenvolverlo manualmente, lo que es molesto.
Solución # 2: instancias flexibles
A costa de la portabilidad, podemos eliminar la restricción por completo con instancias flexibles:
{-# LANGUAGE FlexibleInstances #-}
instance IsString [Word16] where ...
Esta fue la solución que sugirió @ [Daniel Wagner].
(Por cierto, terminé haciendo una envoltura Data.Text.Internal alrededor de Data.Text.Internal y escribiendo el hash encima de eso).