История изменений
Исправление korvin_, (текущая версия) :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, но при этом имеет ту же структуру, тот же «уровень атомарности», это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X,
т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x,
т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP.
С такой семантикой записей есть свои нюансы даже на уровне системы типов, без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.
Исправление korvin_, :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, но при этом имеет ту же структуру, тот же «уровень атомарности», это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X,
т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x,
т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP.
С такой семантикой записей есть свои нюансы даже без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.
Исправление korvin_, :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, но при этом имеет ту же структуру, тот же «уровень атомарности», это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X, т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x, т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP.
С такой семантикой записей есть свои нюансы даже без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.
Исправление korvin_, :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, но при этом имеет ту же структуру, тот же «уровень атомарности», это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X, т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x, т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP. С такой семантикой записей есть свои нюансы даже без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.
Исправление korvin_, :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X, т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x, т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP. С такой семантикой записей есть свои нюансы даже без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.
Исходная версия korvin_, :
Давай-ка вернёмся от множеств к типам. Допустим
type N = 1 | Succ 1
(можно и с нуля, но не это не важно в данном случае)
тогда
type Z = N | 0 | -N
как видишь, тут тип-сумма, это значит, что область значений Z больше, чем N, это значит, что функции, определённые на N являются неопределёнными на некоторой части Z (0 и -N в данном случае), т.е. не каждое значение типа Z можно подставить в функцию, определённую на N, т.е. LSP нарушен.
С другой стороны, рассмотрим типы-произведения (кортежи/записи):
type point_1d_on_x = { x : coord }
type point_2d_on_xy = { x : coord ; y : coord }
абстрагируясь от технических деталей (размещение в памяти и всё такое), видно, что любая функция, ожидающая point_1d_on_x, работающая с координатой X может точно также принять и обработать значение типа point_2d_on_xy, т.к. оно тоже имеет координату X, т.е. point_2d_on_xy может быть подставлен в любую функцию, ожидающую point_1d_on_x, т.е. point_2d_on_xy является подтипом point_1d_on_x по LSP. С такой семантикой записей есть свои нюансы даже без учёта технических деталей (см. “TAPL”, главу про субтипы), поэтому языки, где есть типы структуры/записи/кортежи, её избегают, но можно «сэмулировать» объектами:
class t (x : int) = object
method x = x
end
class s (x : int) (y : int) = object
inherit t x
method y = y
end
let t_to_string t =
Printf.sprintf "%d" t#x
let _ =
let t = new t 1 in
let s = new s 2 3 in
Printf.printf "t = %s, s(t) = %s\n" (t_to_string t) (t_to_string s)
проблема с наследованием в качестве механизма субтипирования только в том, что оно никак не мешает нарушить контракт базового класса в потомке и тем самым нарушить LSP.