Sí, probablemente sería útil. De hecho, todas las versiones ligeramente incompatibles de esto serían útiles. Lo cual es un poco el problema.
No está claro lo que es una clase tan siquiera decir, lo que hace que sea difícil de usar en realidad, porque inevitablemente llegarás a tipos que ofrecen múltiples opciones de valor por defecto, y si no es inmediatamente claro cuál la instancia proporciona, prácticamente pierdes todos los beneficios de tener la clase en primer lugar.
Unos pocos ejemplos:
Para Monoid
casos, se habían obviamente esperan que el elemento de identidad sea la predeterminada. Pero ahora ha vuelto al problema de tantos tipos que tienen dos o más instancias sensibles Monoid
. ¿El valor predeterminado es Integer
0 o 1? Para Monoid
, la biblioteca estándar usa contenedores newtype
, pero estos son torpes y dificultan el trabajo con los tipos envueltos - con Monoid
funciona bien porque tiene acceso a mconcat
y tal, pero no puede hacer nada interesante con solo un valor predeterminado.
Para tipos de Functor
-like con un valor "vacío", que da un valor predeterminado obvio. Esto es lo que están haciendo MonadPlus
y Alternative
... y también se superpone con Monoid
, y si la memoria me sirve, hay al menos un tipo en el que esas tres instancias no son idénticas. ¿Cuál escoges, cuando hay más de una opción? Considere listas: puede agregarlas ciegamente, dando un Monoid
arbitrario, con la lista vacía como identidad; pero para listas de Monoids
también puede zipWith mappend
, dando un monoide levantado con repeat mempty
como identidad. Muchos funtores tienen instancias de Monoid
análogas, pero no siempre ambas; por lo tanto, cualquiera que elijas para las listas, ¡serás conceptualmente inconsistente con alguna otra Functor
!
Para tipos de unidades como ()
, ¡no es difícil elegir un valor predeterminado! Pero, ¿y las enumeraciones? ¿Tiene sentido elegir el primer constructor? Algunas veces, pero no siempre. ¿Cómo sabrá la gente que usa la clase?
¿Qué hay de Bounded
? Si no se aplica ninguna de las opciones anteriores, puede usar minBound
. Pero algunos de los tipos anteriores también pueden ser Bounded
, por lo que confundirá si su valor predeterminado no es su valor mínimo.
Básicamente, sólo hay un solapamiento suficiente que parece tener sentido ... pero en realidad, usted tiene por lo menos tres clases de tipos diferentes en mente aquí, y tratar de unificarlos probablemente no es tan útil como parece al principio.
Si usted puede fijar las cosas un poco mejor y dar una interpretación clara y coherente semántico de un valor "default", sin acaba de reinventar Monoid
u otra clase existente, de manera que la clase de tipo es fácil usar sin tener que detenerse y pensar qué es lo que se elige "predeterminado", ¡excelente! Pero no me ilusionaría hacer que funcione.
Dicho esto, el caso obviamente sensible que no está cubierto por ninguna clase de tipo estándar es singletons como ()
. La mayoría de las veces estos no son terriblemente útiles, ¡por razones obvias!, Que es probablemente la razón por la cual no existe tal clase. Un lugar donde tal clase es extremadamente útil, sin embargo, es cuando estás haciendo algo que involucra travesuras de nivel de tipo, porque tal tipo representa un solo valor en el tipo y nivel de término, entonces una clase para tales tipos le permite manipular los valores de nivel de tipo libremente, luego evoca el término que lo acompaña, por lo que puede pasarlo a alguna otra función que pueda, por ejemplo, seleccionar una instancia de clase de tipo basada en él. Por esa razón, no tengo a class along those lines in my perpetually-incomplete type-hackery library, por ejemplo .:
class TermProxy t where
term :: t
-- This makes explicit the lexical pun of() having type().
instance TermProxy() where
term =()
instance (TermProxy a, TermProxy b) => TermProxy (a, b) where
term = (term, term)
dudo de tal clase es muy útil en cualquier otro contexto, sin embargo.
Gracias por su respuesta generosa a una respuesta tan vaga. En realidad, no estoy interesado en la noción de "valores predeterminados" (¡probablemente no debería haberlo mencionado!), Sino más bien en estas cosas (como las que ocurren en la mano izquierda de una función (o abstracciones) ... Necesito pensar más acerca de cómo quiero usar esta clase. ¡Gracias! – jberryman
@jberryman: Creo que tal vez lo que buscas es una clase de tipos para tipos con exactamente un constructor nullary, entonces? Que incluiría '()', 'Nothing',' [] ', etc., pero no booleanos o números ni nada de eso. Eso está al menos claramente definido y no es tan trivial como los singletons solos. –
Sí, eso es correcto. a lo largo de las líneas de mi ejemplo 'Reader', estoy descubriendo que, para tipos que son receptores o funciones o Cofunctor-ish, puedo definir funciones interesantes para el caso especial'() '., p. 'liftCofunc :: (Cofunctor f) => f() -> f a'. Pensé que sería interesante ver si la función anterior podría definirse de manera más general. Gracias de nuevo. * EDIT *: Supongo que probablemente no sea una coincidencia que 'Cofunctor' tampoco sea una clase estándar;) – jberryman