2016-11-09 9 views
16

난 변경할 수 info 필요 실현 코드OCaml의 분산

module type TS = sig 
    type +'a t 
end 

module T : TS = struct 
    type 'a t = {info : 'a list} 
end 

이 조각을 작성하면과 불변 (a, -'a를 '+).

난 후, 작성 :

module type TS = sig 
    type +'a t 
end 

module T : TS = struct 
    type 'a t = {mutable info : 'a list} 
end 

그러나, 놀람,

Type declarations do not match: 
    type 'a t = { mutable info : 'a list; } 
is not included in 
    type +'a t 
Their variances do not agree. 

아, 내가 약 분산 청각 기억. 공분산반공에 관한 내용이었습니다. 나는 용감한 사람이고, 내 문제 만 혼자 발견 할 것이다!

두 흥미로운 기사 (herehere)를 발견했으며 이해했습니다.

나는

module type TS = sig 
    type (-'a, +'b) t 
end 

module T : TS = struct 
    type ('a, 'b) t = 'a -> 'b 
end 

을 쓸 수 있습니다하지만 궁금하네요. 어떻게 변경 가능한 데이터 유형은 불변이고 단지 공변수가 아닐까요?

내 목록이 바뀔 수 없으므로 'A list('A | 'B) list의 하위 유형으로 간주 될 수 있음을 이해합니다. 함수에 대해 똑같은 함수가있는 경우, 형식이 'A | 'B -> 'C 인 경우 'A -> 'C | 'D 함수의 하위 유형으로 간주 될 수 있습니다. 내 함수가 'A'B을 처리 할 수있는 경우에만 'A을 처리 할 수 ​​있고 반환 만하면 'C님께서는 'C 또는 'D을 꼭 볼 수 있습니다. (단, 'C 님의 사진을보실 수 있습니다).

하지만 배열에 대해? 만약 'A array이 있다면 ('A | 'B) array으로 생각할 수 없습니다. 배열의 요소를 'B으로 수정하면 배열 배열이 잘못 되었기 때문에 실제로는 ('A | 'B) array이고 'A array이 아니기 때문입니다. 그러나 으로 ('A | 'B) array은 어떨까요? 예, 배열에 'B이 포함될 수 있기 때문에 이상하게 보일 것입니다. 그러나 이상하게도 나는 그것이 함수와 같다고 생각했습니다. 어쩌면, 결국, 나는 모든 것을 이해하지 못했지만 그것을 이해하는 데 오랜 시간이 걸렸기 때문에 여기에 내 생각을 말하고 싶었습니다.

TL; DR :

영구 : +'a

함수 : -'a

가변 : 불변 ('a)? 왜 강제로 -'a이 될 수 없습니까?,

type 'a t = {mutable data : 'a} 

let x = {data = 42} 

(* getter *) 
x.data 

(* setter *) 
x.data <- 56 

게터 타입 'a t -> 'a 있습니다 게터와 세터 필드 액세스 및 필드 설정 구문을 사용하여 표현된다

답변

15

난 쉬운 설명을 변경 가능한 값이 두 개의 극한 작업을 가지고 있다고 생각 여기서 'a 유형 변수가 오른쪽에 발생하므로 (공분산 제한 조건을 부과합니다) 형식 자의 유형 변수가 화살표의 왼쪽에서 발생하는 반자동 제약 조건을 부과하는 'a t -> 'a -> unit 유형을 갖습니다. 그래서 우리는 공 변하기 (covariant)와 반 차별 (contravariant) 인 타입을 가지고 있습니다. 즉, 타입 변수 'a은 불변합니다.

+0

나는이 포스트가 미래의 OCaml 개발자들에게이 분산 문제를 쉽고 빠르게 이해하는 데 도움이 될 것으로 기대했던 것과 정확히 같은 종류의 대답을했습니다. – Lhooq

+0

나는 당신이 "and setter * as type ..."을 의미한다고 생각한다. - 편집을 제출했는데, 나는 잘못 고치지 않았 으면 좋겠다. 몇 분 동안 정말 우연히 발견되었습니다. – ELLIOTTCABLE

+0

그래, 확실히 :) 고마워! – ivg

6

유형 t은 기본적으로 가져 오기와 설정의 두 가지 작업을 허용합니다. 비공식적으로, 점점 유형은 'a t -> 'a list이고 설정은 'a t -> 'a list -> unit입니다. 결합하면 'a이 양수 및 음수 위치에서 모두 발생합니다.

[편집 : 다음은 처음에 내가 쓴 것의 (잘하면) 더 명확한 버전입니다. 나는 그것을 우수하다고 생각하기 때문에 이전 버전을 지웠다.]

나는 그것을 더 명백하게하려고 노력할 것이다. subsuper의 적절한 하위 유형이고 witnesssub 유형의 값이 아닌 super 유형의 일부 값이라고 가정합니다. 이제 f : sub -> unitwitness 값에 실패하는 함수로 보자. 유형 안전은 witnessf으로 전달되지 않도록합니다. 예를 들어 sub t을 하위 유형으로 super t으로 처리하거나 다른 방법으로 처리 할 수있는 경우 유형 안전성에 실패하는 것으로 표시합니다. 그래서 sub t의 하위 유형으로 super t 치료

let v_super = ({ info = [witness]; } : super t) in 
let v_sub = (v_super : sub t) in (* Suppose this was allowed. *) 
List.map f v_sub.info (* Equivalent to f witness. Woops. *) 

은 허용 될 수 없다. 이것은 이미 알고있는 공분산을 보여줍니다. 이제 contravariance. super t의 하위 유형으로 sub t을 치료 그래서

let v_sub = ({ info = []; } : sub t) in 
let v_super = (v_sub : super t) in (* Suppose this was allowed. *) 
v_super.info <- [witness]; 
    (* As v_sub and v_super are the same thing, 
     we have v_sub.info=[witness] once more. *) 
List.map f v_sub.info (* Woops again. *) 

는 contravariance을 보여주는 중 하나를 허용 할 수 없습니다. 함께, 'a t은 변하지 않습니다.

+0

글쎄, 당신의 대답은 ivg의 대답을 풀어주는 것과 같지만 좋은 추가 ;-) – Lhooq