2012-06-19 9 views
15

Soy un novato de Haskell. Me he dado cuenta de que Haskell no admite nombre de registro de sobrecarga:¿Por qué Haskell/GHC no admite la sobrecarga del nombre de registro?

-- Records.hs 

data Employee = Employee 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    } deriving (Show, Eq) 

data Manager = Manager 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    , subordinates :: [Employee] 
    } deriving (Show, Eq) 

Cuando compilo este recibo:

[1 of 1] Compiling Main    (Records.hs, Records.o) 

Records.hs:10:5: 
    Multiple declarations of `firstName' 
    Declared at: Records.hs:4:5 
       Records.hs:10:5 

Records.hs:11:5: 
    Multiple declarations of `lastName' 
    Declared at: Records.hs:5:5 
       Records.hs:11:5 

Records.hs:12:5: 
    Multiple declarations of `ssn' 
    Declared at: Records.hs:6:5 
       Records.hs:12:5 

Teniendo en cuenta la "fuerza" del sistema de tipos de Haskell, parece que debería ser fácil para el compilador determinar a qué campo acceder en

emp = Employee "Joe" "Smith" "111-22-3333" 
man = Manager "Mary" "Jones" "333-22-1111" [emp] 
firstName man 
firstName emp 

Hay algún problema que no estoy viendo. Sé que el Informe Haskell no permite esto, pero ¿por qué no?

+1

Esto no es en absoluto una respuesta a su pregunta, pero usualmente dividir los tipos de datos en módulos separados cada vez que surja una situación como la suya. Podría, por ejemplo, hacer un módulo 'Empleado' y un módulo' Administrador', e importarlos calificados como digamos 'E' y' M' respectivamente, y luego usar 'E.firstName',' M.firstName', etc. Esto me da una sintaxis razonablemente agradable. (No estoy diciendo que esto sea necesariamente una buena idea, pero es lo que terminé haciendo y resultó muy bien en mis casos). – gspr

+3

Sí, pero esto parece ser un "kludge" en un lenguaje por lo demás elegante. – Ralph

Respuesta

9

El sistema de registro actual no es muy sofisticado. Es principalmente algo de azúcar sintáctico para cosas que podrías hacer con repetitivo si no hubiera sintaxis de registro.

En particular, esto:

data Employee = Employee 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    } deriving (Show, Eq) 

genera (entre otras cosas) una función firstName :: Employee -> String.

Si también permite que en el mismo módulo de este tipo:

data Manager = Manager 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    , subordinates :: [Employee] 
    } deriving (Show, Eq) 

entonces ¿cuál sería el tipo de la función firstName?

Tendría que haber dos funciones separadas sobrecargando el mismo nombre, que Haskell no permite. A menos que imagine que esto generaría implícitamente una clase de tipo y crearía instancias de ella para todo con un campo llamado firstName (se vuelve desordenado en el caso general, cuando los campos podrían tener diferentes tipos), entonces el sistema de registro actual de Haskell no va a ser capaz de soportar múltiples campos con el mismo nombre en el mismo módulo. Haskell ni siquiera intenta hacer algo así en el presente.

Podría, por supuesto, hacerse mejor. Pero hay algunos problemas difíciles de resolver, y esencialmente nadie ha encontrado soluciones para ellos que hayan convencido a todos de que todavía hay una dirección más prometedora para mudarse.

+0

Supongo que puede crear una clase de tipo y luego hacer que los métodos de clase de tipo llamen a las versiones de registros específicos, pero eso agregaría una gran cantidad de texto repetitivo a un idioma que afortunadamente no lo necesita. – Ralph

+1

Si permite la sobrecarga de campo, entonces la función 'firstName' debe tener el tipo' forall a. a'. Con inferencia de tipo o declaración de tipo explícita, este tipo debe ser especializado. Los constructores de registros en Agda funcionan así. – JJJ

4

Una opción para evitar esto es poner sus tipos de datos en diferentes módulos y usar importaciones calificadas. De esta forma, puede utilizar los mismos descriptores de campo en diferentes registros de datos y mantener su código limpio y legible.

Puede crear un módulo para el empleado, por ejemplo

module Model.Employee where 

data Employee = Employee 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    } deriving (Show, Eq) 

y un módulo para el Administrador, por ejemplo:

module Model.Manager where 

import Model.Employee (Employee) 

data Manager = Manager 
    { firstName :: String 
    , lastName :: String 
    , ssn :: String 
    , subordinates :: [Employee] 
    } deriving (Show, Eq) 

Y donde quiera que desee utilizar estos dos tipos de datos puede importarlos calificados y acceder a ellos de la siguiente manera:

import   Model.Employee (Employee) 
import qualified Model.Employee as Employee 
import   Model.Manager (Manager) 
import qualified Model.Manager as Manager 

emp = Employee "Joe" "Smith" "111-22-3333" 
man = Manager "Mary" "Jones" "333-22-1111" [emp] 

name1 = Manager.firstName man 
name2 = Employee.firstName emp 

Tenga en cuenta que después todo lo que está usando son dos tipos de datos diferentes y, por lo tanto, Manger.firstName es otra función que Employee.firstName, incluso cuando sabe que ambos tipos de datos representan a una persona y cada persona tiene un nombre. Pero depende de usted la distancia que vaya hasta los tipos de datos abstractos, por ejemplo, para crear un tipo de datos Person a partir de esas "colecciones de atributos" también.

Cuestiones relacionadas