2012-05-22 12 views
6

Chcę zdefiniować typ, tak aby cała konstrukcja przechodziła przez elementy modułu, które mogą zachować niezmienniki, ale zezwalać na destrukturyzację w celu dopasowania wzorca.Zachowanie niezmienności przy jednoczesnym umożliwieniu destrukturyzacji

Właśnie uczę SML ale po prawie działa dla int pary z niezmienna, że ​​lewica powinna być ściśle mniej niż prawo

module Range : sig 
    type t = private { left:int; right:int } 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
end = struct 
    type t = { left:int; right:int } 
    exception InvalidRange of (int*int) 
    let make left right = if left < right 
    then { left; right } 
    else raise (InvalidRange (left, right)) 
end 

który działa w ten sposób

# let p = Range.make 1 2;; 
val p : Range.t = {Range.left = 1; Range.right = 2} 
# let q = Range.make 2 1;; 
Exception: Range.InvalidRange (2, 1). 

i destrukturyzacja działa po mody

# let {Range.left=x; Range.right=y} = p;; 
val x : int = 1 
val y : int = 2 

podczas konstruowania nie spełnia

# let badp = {Range.left = 2; Range.right = 1};; 
    let badp = {Range.left = 2; Range.right = 1};; 
Error: Cannot create values of the private type Range.t 
# open Range;; 
# let badp = {left = 2; right=1};; 
    let badp = {left = 2; right=1};; 
Error: Cannot create values of the private type Range.t 

Ale naprawdę chciałbym zrobić, to mieć syntaktyczną wygodę destrukcji krotek. Poniższa nie działa:

module Range : sig 
    type t = private int*int 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
end = struct 
    type t = int*int 
    exception InvalidRange of (int*int) 
    let make left right = if left < right 
    then (left, right) 
    else raise (InvalidRange (left, right)) 
end 

ale wtedy nie mogę destructure go za pomocą wzoru krotka:

# let r = Range.make 1 2 ;; 
val r : Range.t = (1, 2) 
# let (a, b) = r;; 
    let (a, b) = r;; 
Error: This expression has type Range.t 
     but an expression was expected of type 'a * 'b 

mogę zmienić typ do type t = R of (int * int) ale muszę to być tak światło- w miarę możliwości należy pamiętać o wadze. Jakieś pomysły?

Odpowiedz

9

Jak wyjaśniono w manual, potrzebny jest wyraźny przymus:

# let (a, b) = (r :> int*int);; 
val a : int = 1 
val b : int = 2 
+0

Dziękujemy za wskaźnik do instrukcji. Zajmie mi to trochę czasu, gdy zawiąże się między typem nominalnym a wymazanymi typami, a gdy nie. Czy obecność operatora wymuszania typu oznacza, że ​​destrukturyzacja jest zawsze możliwa - więc nie ma możliwości ukrywania prawdziwych informacji (np. Mutables lub sekretów) za wyjątkiem wartości zamkniętych przez funkcje? –

+2

Występuje różnica między 1) typami przezroczystymi, 2) typami abstrakcyjnymi, 3a) wariantami prywatnymi lub typami rekordów 3b) skrótami typu prywatnego. Dla 1) przymus jest możliwy w obie strony i jest niejawny. W przypadku 2) w ogóle nie jest możliwy przymus. W przypadku 3a) przymus jest możliwy w jedną stronę i wydaje się być ukryty. W przypadku 3b) przymus jest również możliwy w jedną stronę i wydaje się być wyraźny. Ważne jest, aby pamiętać, że koercjony nie mają zawartości obliczeniowej (są usuwane w czasie wykonywania). – esope

+1

dzięki. Tak więc system typu jest dwuwarstwowy - fundamentalny, który ma rozróżnienie między typami konkretnymi i abstrakcyjnymi, który wpływa na wysyłkę wiadomości i powierzchowną, którą można usunąć, która może wykluczyć wysyłkę, ale nie ma na nią wpływu. –

2

Właśnie uruchomiono test z Objsize (zgłasza rozmiary wartości SML).

# type fancy = R of int * int;; 
type fancy = R of int * int 
# Objsize.objsize (R (3, 5));; 
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0} 
# Objsize.objsize (3,5);; 
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0} 

Jeśli uważasz, że te wartości (co ja), nie ma kary rozmiar za pomocą własnego typu single-konstruktora zamiast krotki.

+0

Nie sądzę, że odpowiadasz na pytanie. – Thomash

+3

Próbowałem odpowiedzieć na część, w której mówi: "Mogłabym zmienić typ na typ t = R (int * int), ale potrzebuję, aby były jak najmniejsze, jak to tylko możliwe. Twierdzę, że 'R int * int' nie jest typem pamięci masywnej. –

4

Prostym sposobem na to jest dodanie funkcji to_tuple i przekształcenie typu wycieczki w streszczenie.

module Range : sig 
    type t 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
    val to_tuple : t -> (int * int) 
end = struct 
    type t = { left:int; right:int } 
    exception InvalidRange of (int*int) 

    let make left right = if left < right 
    then { left; right } 
    else raise (InvalidRange (left, right)) 

    let to_tuple t = t.left, t.right 

end 

wtedy można zrobić

let (a, b) = to_tuple range 
+2

Twoje rozwiązanie działa. Różnica w porównaniu z typem prywatnym polega na tym, że 'to_tuple' ma zawartość obliczeniową, podczas gdy przymus (_:> _) nie ma takiej. Aby to zrozumieć, rozważ wartość typu 'Range.t list' i spróbuj przekonwertować go na listę typu' (int * int) list'. Za pomocą twojego rozwiązania musisz użyć 'List.map to_tuple _' (który kopiuje listę); z prywatnym rozwiązaniem typu piszemy tylko listę '(_:> (int * int)), która nic nie robi (wymazywane są koercje). – esope

3

Twoje rozwiązanie type t = R of (int * int) będzie lekka pod względem pamięci, a jest składniowo nieco lżejsze-waga niż roztwór przymusu. OCaml optymalizuje przypadek typu danych jednego konstruktora, więc nie płacisz za to. (Nie mam oficjalnego odniesienia do tego twierdzenia, ale Adam Chlipala (ekspert OCaml) wspomina o tym tutaj: http://adam.chlipala.net/cpdt/html/Subset.html).

Powiązane problemy