2012-06-19 1 views
15

저는 하스켈 초보자입니다. 내가 받기를 컴파일 할 때왜 Haskell/GHC는 레코드 이름 오버로드를 지원하지 않습니다

-- 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) 

: 나는 하스켈은 레코드 이름 오버로드를 지원하지 않는 것으로 나타났습니다 그것이 있어야처럼

[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 

는 하스켈 타입 시스템의 "힘"을 감안할 때, 그것은 보인다 컴파일러가 액세스 할 필드를 결정하기가 쉽습니다.

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

내가 보지 못하는 문제가 있습니까? 나는 하스켈 보고서가 이것을 허용하지 않는다는 것을 알고 있지만 왜 그렇지 않은가?

+1

귀하의 질문에 대한 답변이 아니지만 귀하의 상황이 발생할 때마다 데이터 유형을 별도의 모듈로 분리합니다. 예를 들어,'Employee' 모듈과'Manager' 모듈을 만들고'E'와'M'으로 정규화 된 것을 가져온 다음'E.firstName','M.firstName' 등을 사용할 수 있습니다 이것은 합리적으로 멋진 구문을 제공합니다. (나는 이것이 반드시 좋은 생각이라고 말하는 것이 아니지만, 내가 끝내고 그것이 나의 경우에 훌륭하게 밝혀 졌다는 것을 의미한다). – gspr

+3

그래,하지만이게 다르게 우아한 언어의 "덩어리"같아. – Ralph

답변

9

현재 레코드 시스템은 그리 정교하지 않습니다. 레코드 구문이없는 경우 상용구를 사용하여 수행 할 수있는 대부분의 구문 론적 설탕입니다. 특히

이 :

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

함수 firstName :: Employee -> String (다른 것들 중에서)를 생성한다.

당신은 또한 같은 모듈이 유형 허용하는 경우 :

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

다음 무엇을 firstName 함수의 유형이 될 것입니까?

하스켈이 허용하지 않는 동일한 이름의 오버로드 된 두 개의 별도 함수가 있어야합니다. 이것이 암시 적으로 typeclass를 생성하고 firstName이라는 필드 (모든 필드가 다른 유형을 가질 수있는 일반적인 경우 엉망이 됨)로 모든 인스턴스에 대해 인스턴스를 만들 것이라고 상상하지 않으면 Haskell의 현재 레코드 시스템이 될 수 없습니다 동일한 모듈에서 같은 이름의 여러 필드를 지원할 수 있습니다. 하스켈은 현재 그러한 일을 시도하지 않습니다.

물론 더 잘 수행 될 수 있습니다. 그러나 해결해야 할 몇 가지 까다로운 문제가 있으며 본질적으로 누구에게도 가장 유망한 방향이 있다고 모든 사람들에게 확신시켜 준 해결책을 제시하지 못합니다.

+0

typeclass를 만든 다음 특정 레코드 버전을 typeclass 메서드로 호출 할 수는 있지만 일반적으로 필요없는 언어에 많은 상용구를 추가하게됩니다. – Ralph

+1

필드 오버로딩을 허용하면'firstName' 함수는'forall a. a'. 형식 유추 또는 명시 적 형식 선언을 사용하면이 형식을 특수화해야합니다. Agda의 레코드 생성자는 이와 같이 작동합니다. – JJJ

4

데이터 유형을 다른 모듈에 저장하고 정규화 된 가져 오기를 사용하는 것이 하나의 옵션입니다. 그런 식으로 다른 데이터 레코드에서 동일한 필드 접근 코드를 사용하고 코드를 깨끗하고 읽기 쉽게 유지할 수 있습니다.

당신은 예를

module Model.Employee where 

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

그리고 예를 들어, 관리자에 대한 하나 개의 모듈을 위해, 직원에 대해 하나 개의 모듈을 만들 수 있습니다

:

module Model.Manager where 

import Model.Employee (Employee) 

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

그리고이 두 가지 데이터 유형을 사용하여 원하는 목적지 자격을 가져 와서 다음과 같이 액세스 할 수 있습니다.

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 

모두 두 개의 서로 다른 데이터 형식을 사용하고 있으므로 Manger.firstName은 두 데이터 형식이 사람을 나타내고 각 사람이 이름을 가지고 있다고하더라도 Employee.firstName보다 다른 함수입니다. 그러나 추상적 인 데이터 유형에 얼마나 멀리 가야하는지, 예를 들어 그러한 "속성 콜렉션"에서 Person 데이터 유형을 작성하는 것까지 당신에게 달려 있습니다.