[Moscow.pm] Строгая типизация в Perl

Андрей П. Ковбович akovbovich на gmail.com
Пн Сен 3 02:22:38 PDT 2012


2 сентября 2012 г., 7:01 пользователь Nikita Zubkov
<nikzubkov на gmail.com> написал:
> Как этот пример связан с изначальным тезисом о защите от конкатенации СТРОК
> в разных кодировках?

Для начала, строкой я называю последовательность символов некого
алфавита, а не тип. Строки представлены типами, отражающую их
кодировку, в том числе абстрактными, скрывающими свое внутренее
представление. Операции (в том числе конкатенация) между
несовместимыми типами пресекаются на этапе тайп чека, как вы уже
видели.

В примере, примитивный тип string - по сути массив однобайтовых
символов. Для однобайтовых кодировок его вполне достаточно. Как только
у вас появляется юникод, понятно, что многие операции со строками
между однобайтовыми и многобайтовыми кодировками не имеют смысла, вот
тогда и вводятся новые типы для кодировок utf8,16,32. И уже система
типов следит за совместимостью операций между latin1|cp1251 и utf8
кодированными строками и она не даст вам их склеить, так как в этом
нет смысла.

> Да и Вы же сами в конце показали как прекрасно конкатенируютя СТРОКИ в utf8 и latin1.

Потому что перед конкатенацией, я преобразовал (сериализовал) юникод
строку в значение типа string (байтовый массив - базовый тип,
согласен, название типа спорно).
val encode : CharEncoding.t -> text -> string
Понятно, что без дополнительных ограничений со стороны системы типов
эта операция будет считаться валидной. Тут уж ответственность на
программисте, контролировать то, что внутри байтовой
последовательности. Опять же, при желании, вы можете настроить ее так,
чтобы это было невозможно.

module type API = sig
    type uni
    type nonuni
    type 'a str
    val nonuni : string -> nonuni str
    val uni : string -> uni str
    val show : 'a str -> string
    val ( ^ ) : nonuni str -> nonuni str -> nonuni str
  end

module MyStrings : API = struct
    type nonuni
    type uni
    type 'a str = string
    let nonuni s = s
    let uni s = s
    let show s = s
    let ( ^ ) = Pervasives.( ^ )
  end

# open MyStrings;;
# let u, u', n, n' = uni "hello", uni "world", nonuni "hello", nonuni "world";;
val u : MyStrings.uni MyStrings.str = <abstr>
val u' : MyStrings.uni MyStrings.str = <abstr>
val n : MyStrings.nonuni MyStrings.str = <abstr>
val n' : MyStrings.nonuni MyStrings.str = <abstr>

(* nonuni str + nonuni str = ok *)
# show (n ^ n');;
- : string = "helloworld"

(* uni str + nonuni str = fail *)
# show (u ^ n');;
Error: This expression has type MyStrings.uni MyStrings.str
       but an expression was expected of type MyStrings.nonuni MyStrings.str

(* uni str + uni str = fail *)
# show (u ^ u');;
Error: This expression has type MyStrings.uni MyStrings.str
       but an expression was expected of type MyStrings.nonuni MyStrings.str

Раз уж я привел в пример библиотеку для работы с юникодом, если
позволите, переформулирую первоначальный тезис, чтобы оставить в покое
однобайтовые кодировки - вы не сможете склеить по ошибке utf8 и utf16
строки без явного кастинга (строгая типизация в действии).


Подробная информация о списке рассылки Moscow-pm