LINUX.ORG.RU

История изменений

Исправление 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.