Comments 119
Вы что как ребенок, никакого скандала не будет. Скандал разгорается только тогда это группа людей голосует за демпартию.
пока не разгорелся очередной скандал связанный с дискриминацией людей с фамилиями
Вот если бы она была темнокожей, то да… А так — тьфу, полгода никто так и не пошевелился.
Надо девушке выйти замуж за парня с фамилией False.
PS: Языки без строгой типизации лучше говорили они…
Там скорее всего дело не в типизации, а в валидаторе значений.
Просто так не получится — кроме фамилии Total у него и имя должно быть Value!
:)
«Запрещается запись имени ребенка, которое состоит из цифр, буквенно-цифровых обозначений, числительных, символов и не являющихся буквами знаков, за исключением знака „дефис“, или их любой комбинации либо содержит бранные слова, указания на ранги, должности, титулы», — так звучат изменения в статью 18 закона «Об актах гражданского состояния»
Так что в Сумму Итого вполне себе можно переименоваться. Причем судя по всему есть баг, что это относится к имени ребенка при рождении, а в дальнейшем случаются и должности в имени например «Президент России Викторовна»
lenta.ru/news/2021/02/20/prezident_rossii
— Конечно. Как Вас зовут сейчас?
— Пётр Говно.
— О, понятно. А на что хотите поменять?
— Хочу быть Виталиком…
На Хабре все время приводят тупой вариант, где мама говорит «Да, мы зовем его Робин-Брось-Таблицу» — а что, нормальное же имя, как для переводчика?
Вот, собственно:

Может и тупо, но с "Дропика" я кринжанул далеко не так сильно, как с Робина. Глобализация, все дела, заимствования вроде "Эдвард-руки-ножницы" и «Двейн "Скала" Джонсон» уже не так сильно смущают, а вот чудаковатое "Дропик" — это откровенно грустно. Да и в целом перевод с xkcd.ru выглядит профессиональней.
Она ещё хорошо отделалась. А вот если бы где-то в гос. учреждении, или в банке — беда, пиши пропало.
Надо было кириллическую е использовать, да и всё
Вряд ли, со слабой динамической типизацией она просто на каком-то шаге стала бы "Rachel 1".
Это фамилия, тут вообще на всех шагах должна быть строка, поэтому видимо дело в протоколе обмена. В JSON логические значения по синтаксису отличаются от строк, скорее всего у них там кривой парсинг XML.
<IsVerified>True</IsVerified>
<LastName>True</LastName>
Вроде бы через XSD это как-то решается.
скорее всего у них там кривой парсинг XML.
Ну, вообще в XML только строки и ничего более. И при парсинге надо приводить текст ноды к желаемому типу. Т.е. программист должен заранее знать, что нода с именем IsVerified — хранит булево значение, а LastName — строковое.
Это если у вас статическая типизация, и вы каждую схему вручную обрабатываете. А с динамической типизацией можно сделать универсальную функцию parseXml(), которая будет true/false парсить в bool, числа в int, а все остальное в строку, и складывать в ассоциативный map "название поля — значение". Видимо они так и сделали, возможно для совместимости с JSON.
В статью скриншот добавили, ошибка еще глупее оказалась, там специальная проверка на строку "true".
Можно, но это сложнее, и вы не сможете просто писать string name = xmlObject.name; bool isVerified = xmlObject.isVerified;
Что-то типа такого.
<?php
declare(strict_types=1);
function parseXml(string $xml)
{
$xmlObj = new stdClass();
$xml = trim($xml);
preg_match('#^\<(\w*)\>#smu', $xml, $matches);
$rootName = $matches[1];
preg_match("#^\\<{$rootName}\\>(.*)\\<\\/{$rootName}\\>#smu", $xml, $matches);
$content = trim($matches[1]);
while (true) {
preg_match('#^\<(\w*)\>#smu', $content, $matches);
$elementName = $matches[1];
preg_match("#^\\<{$elementName}\\>(.*)\\<\\/{$elementName}\\>#smu", $content, $matches);
$value = trim($matches[1]);
if (preg_match('#^\d+$#', $value)) {
$value = (int)$value;
} elseif (preg_match('#^true|false$#', $value)) {
$value = ($value === 'true' ? true : false);
}
$xmlObj->$elementName = $value;
$content = trim(substr($content, strlen($matches[0])));
if ($content === '') break;
}
return $xmlObj;
}
$xml = '
<root>
<id>123</id>
<firstName>test</firstName>
<lastName>true</lastName>
<isVerified>true</isVerified>
</root>
';
$xmlObj = parseXml($xml);
var_dump($xmlObj);
/*
class stdClass#1 (4) {
public $id => int(123)
public $firstName => string(4) "test"
public $lastName => bool(true)
public $isVerified => bool(true)
}
*/
class User
{
public string $lastName;
public bool $isVerified;
}
$user = new User();
$user->isVerified = $xmlObj->isVerified;
$user->lastName = $xmlObj->lastName;
// PHP Fatal error: Uncaught TypeError: Typed property User::$lastName must be string, bool used
Ну да, и? PHP это язык с динамической типизацией, тип существует в рантайме вместе со значением переменной. Поэтому можно создать произвольный xmlObject, не описывая его предварительно специальным классом, и свойства у него будут иметь разный тип. То есть программист не "должен заранее знать, что нода с именем IsVerified — хранит булево значение, а LastName — строковое", и он может написать универсальную функцию для парсинга произвольного XML. А дальше уже ее результаты можно использовать в коде, где есть контроль типов.
Ну так и в языке со статической типизацией у вас будет что-то такое
Ну напишите что-то такое на C++, C# или Java.
data Node = TextNode Text | NumberNode Number | CDataNode Blob
А где тут string, int и boolean, чтобы можно было задавать значения полям класса с такими типами?
И вы как-то реализацию parseXML многозначительно за многоточием спрятали, и вообще она у вас не используется.
я могу в своём менее выразительном хаскеле написать
A.FromJSON
Угу, когда за вас функцию произвольного парсинга уже кто-то написал, конечно ее писать не надо) Но мы-то говорим о ее реализации.
MyData { lastName :: String, isVerified :: Bool }
Но это, как я понимаю, и есть описанный предварительно специальный класс. В моем примере класс User нужен просто для демонстрации, без него парсинг тоже нормально работает. Вместо него $xmlObj->lastName
можно было передать в функцию, принимающую string.
Ну, если серьёзно, зачем выбирать языки с недостаточно выразительной системой типов?
Потому что когда я сказал "Это если у вас статическая типизация, и вы каждую схему вручную обрабатываете", я подразумевал наиболее распространенные. То есть те, в которых утверждение "при парсинге надо приводить текст ноды к желаемому типу и программист должен заранее знать", на которое я отвечал, верно. В них оно верно именно из-за статической типизации, потому что настолько же или даже менее выразительная динамическая дает возможность такую функцию написать.
Ну вместо String там Text, например.
А, ок, я подумал, что TextNode это тип, а Text это название поля.
А вот дальше вы проверяете конкретный тип конкретной ноды в конкретном контексте, который вам нужен
Ну а в моем примере не надо проверять, в том и смысл.
Такие проверки
case (findNode "foo/bar", findNode "baz/quux") of
(Just (NumberNode n1), Just (NumberNode n2)) -> Just (n1 + n2)
это и есть "можно, но это сложнее", и вы не можете просто писать
int sum = findNode(xml, "foo/bar") + findNode(xml, "baz/quux")
.
В C++ будут такие же проверки типа ноды
if (node1->type == TYPE_NUMBER && node2->type == TYPE_NUMBER) sum = (*(int*)node1->pValue) + (*(int*)node2->pValue);
.
Я именно их и подразумевал. И поэтому проще заранее знать "какие ноды что хранят" и "приводить текст ноды к желаемому типу" из строки по месту
int sum = toInt(xml->getNodeValue("foo/bar")) + toInt(xml->getNodeValue("baz/quux"))
.
Так в том и суть: её написать можно, даже в языке со статической типизацией,
Такую, чтобы не писать везде "case of Just NumberNode" нельзя. В том и была суть. Такие проверки это фактически и есть реализация динамической типизации.
Это не "парсить в int/string/bool и складывать в ассоциативный map", а "парсить в NodeType и складывать в ассоциативный map<string, NodeType>". Со вторым я и не спорил, что это возможно.
это ж не предоставляемый компилятором интринсик.
В данном случае компилятором предоставляется система типов, которая немного упрощает ее написание по сравнению с другими языками со статической типизацией.
Да, и при этом в компилтайме вы не узнаете, что вы делаете ерунду.
В вашем коде проверка case of NumberNode
выполняется в рантайме, так же как и в PHP. В плане проверки типов тут вообще разницы нет. Она будет только если lastName будет иметь тип, отличный от string | int | bool
, тогда да, в Haskell компилятор скажет, что такого типа нет в списке возможных типов ноды, поэтому присвоить значение нельзя. Но мы же не об этом случае говорим.
либо у вас такое место встречается больше одного раза, и тогда вы пишете тип данных вроде того, что выше
Ну вот в PHP его можно не писать, и все равно иметь проверки типов во всех местах использования.
Так это вроде не противоречит моему исходному тезису, разве нет?
Я написал "А с динамической типизацией можно сделать универсальную функцию parseXml()", вы на это возразили, что и со статической можно. Но со статической именно в таком виде сделать нельзя, тем более в мейнстримных языках, хотя можно сделать в другом, чтобы работало так же. "Можно" в моем высказывании можно читать как "гораздо проще". Так-то понятно, что динамический PHP написан на статическом C, и значит принципиальная возможность есть, просто код, который надо писать программисту, отличается.
Но вы там явно расставляете аннотации string/bool/etc, разве нет?
Ну да, проверка типов же подразумевает что типы указаны в 2 местах, и мы их сравниваем. Только писать "тип данных вроде того, что выше" не нужно, объектом такого типа данных является сам xmlObject. Никакого объединенного типа NodeType = string|int|bool в коде программиста не существует, он работает сразу с конкретными типами.
можно написать несколько функций типаasString :: Node -> Maybe Text
isVerified <- asBool =<< findNode "baz/quux" xml
Это и есть "при парсинге надо приводить текст ноды к желаемому типу. Т.е. программист должен заранее знать, что нода с именем IsVerified — хранит булево значение, а LastName — строковое".
Чем она плоха?
Тем, что тип значений не string или bool, а NodeType, который type union всех возможных, и поэтому конкретный тип надо проверять вручную через case of.
Я же говорил не о том, что парсить XML со статической типизацией вообще невозможно, а что нельзя это сделать так же просто и наглядно, как с динамической.
Давайте сделаем более конкретно, а то я не очень понимаю, что вы имеете в виду. Возьмем JSON, с ним попроще. Вот есть работающий код на PHP.
declare(strict_types=1);
class User {
public string $lastName;
}
function checkLastName(string $lastName) {
return true;
}
function run(object $jsonObj) {
$user = new User();
$user->lastName = $jsonObj->lastName;
checkLastName($jsonObj->lastName);
}
function parseJson(string $json): object {
return json_decode($json);
}
$json = '{
"id": 123,
"firstName": "test",
"lastName": true,
"isVerified": true
}';
$jsonObj = parseJson($json);
run($jsonObj);
// TypeError: Typed property User::$lastName must be string, bool used
Напишите полностью аналогичный код на Haskell. Критерии — parseJson() возвращает объект, он передается в другую функцию, его свойство используется и при установке свойства в объекте User, и при передаче в функцию checkLastName(), без повторного парсинга или объявления промежуточного класса с известной структурой. Структура User отличается от структуры JSON, а ошибка из статьи воспроизводится, так как в JSON lastName имеет тип bool. До checkLastName() в этом коде выполнение не доходит, но если закомментировать предыдущую строчку, там тоже будет ошибка типов.
Так будет проще сравнить объем кода и где какие проверки.
попытаться проинтерпретировать ноду как строку, а, если не получится, как дату
Как вы это сделаете на php?
Так в PHP не надо это делать, в дереве xml
уже готовые для использования типы, а не обобщенный NodeType. Я не очень понял почему нода может быть не строкой, если в xml изначально все значения тегов строковые, но если вы подразумеваете, что там после разбора будет int timestamp, который можно конвертировать в дату, то на PHP будет такая же проверка типов.
$value = is_string($xml->quux->bar) ? $xml->quux->bar : (new DateTime('@' . $xml->quux->bar))->format('Y-m-d H:i:s');
Реализация findNode для конкретного значения ничем не отличается от задания специального класса-схемы с геттером, я и говорю, что в PHP это не нужно.
Именно что не при парсинге, а после.
Ну в той ситуации в коде будут всегда строковые значения, которые надо конвертировать по месту к нужному типу, а у вас в коде обобщенный NodeType, из которого вы в коде достаете по месту нужный тип. Для программиста разницы нет, хотя в рантайме значения отличаются. Мой изначальный коммент как раз и был про то, что программисту надо вручную доставать нужный тип.
Что не отличается от соответствующего знания вашего php-программиста в момент написания класса User.
Отличается. В момент написания класса User вообще может еще не быть никакой работы с XML. В том утверждении речь идет про ноды в XML, а не про свойства класса. И класс User не нужен, я могу просто сделать var_dump(), и он покажет, что в одном свойстве string, а в другом bool.
В ином же случае я не понимаю претензии, потому что у вас там тоже в каком-то смысле юнион, просто он скрыт от программиста.
Так в том и смысл. Технические детали скрыты от программиста, так же как машинные команды или счетчик в foreach ($list as $element)
. Зачем мне работать с обобщенным NodeType, если мне нужны строки и инты. Язык с динамической типизацией скрывает детали реализации, которые со статической программист пишет сам.
В самом деле, зачем вам в вашем клиентском (для библиотеки) коде сначала руками парсить XML или жсон в какой-то объект, потом руками из него что-то доставать, если можно сразу распарсить в нужную структуру?
Затем что не всегда в точке парсинга есть нужная структура, а в точке использования структуры зависимость для парсинга. И если так делать, то слишком много зависимостей по коду надо раскидывать. А так у нас в место использования передается ассоциативный массив, который вы можете хоть парсингом XML получить, хоть парсингом JSON, хоть вручную в тестах захардкодить.
Тут я сразу сдаюсь: ошибка типов там будет (если их не проверять) независимо от комментирования предыдущей строчки.
Это было просто пояснение про работу этого кода. Но я не понимаю, что вы имеете в виду. У вас точно так же ошибка возникает в рантайме, когда вы запускаете parseJson json >>= run "lastName"
, и выдает только ошибку про первую строчку Left "key lastName has unexpected type"
.
lastName <- maybeToRight "key not found or has wrong type" $ obj ^? key keyName . _String
Ну вот вы и нарушили критерий "его свойство используется и при установке свойства в объекте User, и при передаче в функцию checkLastName()". Вы используете не свойство объекта, а промежуточную переменную. Причем сделали кастинг к заранее известному типу string, это нарушает другой критерий "без объявления промежуточного класса с известной структурой". Формально это не класс конечно, а можно сказать только одно поле, но принципиально это ничего не меняет.
Полный эквивалент был бы такой.
let user = User (obj ^? key keyName)
pure $ checkLastName (obj ^? key keyName)
Вот как раз про это я и говорил, что в языках со статической типизацией так написать нельзя.
lastName <- maybeToRight [i|key #{keyName} has unexpected type|] $ lastNameObj ^? _String
Так это ж проверка типа вручную, которую по условиям надо было избежать. С сильной динамической типизацией это делает сама система типизации.
Ну так я ж говорю, я не говорил, что принципиально невозможно написать какую-то обобщенную функцию парсинга, я имел в виду, что нельзя написать именно такую и пользоваться ей именно так, как в языках с динамической типизацией.
В целом да, только надо учитывать, что один string это тип, а другой string функция кастинга. Больше сущностей, с которыми надо работать программисту. И сам подход отличается, мы проверяем сами, а не полагаемся на автоматическую проверку типов.
Ну и саму реализацию функции парсинга можно отметить. Со статической типизацией ее объем будет гораздо больше.
В C++ будут такие же проверки типа ноды
if (node1->type == TYPE_NUMBER && node2->type == TYPE_NUMBER) sum = (*(int*)node1->pValue) + (*(int*)node2->pValue);.
Я именно их и подразумевал. И поэтому проще заранее знать «какие ноды что хранят» и «приводить текст ноды к желаемому типу» из строки по месту
int sum = toInt(xml->getNodeValue(«foo/bar»)) + toInt(xml->getNodeValue(«baz/quux»)).
В c++ это тоже реализуется елементрано. Достаточно лишь немного углубиться в ООП.
Например: создаём абстрактный класс Node, с множеством методов:
asString, asBool, asInt, и так далее.
Далле создаём наследуемые классы для каждого типа данных (StringNode, BoolNode,...), и в них реализовываем преобразования в соответствующих методах. Если преобразовать указаную ноду в тот или иной тип невозможно, то посылаем исключение. В результате вышеописаный код можно изобразить так:
try{
int sum = node1->asInt() + node2->asInt();
...
}catch(...){
cout << "Type error";
}
Кто-то в шутку сказал, что самым быстрым решением для Рэйчел будет выйти замуж и взять фамилию супруга, но предостерегли, что могут быть проблемы, если у супруга будет фамилия «Null» или «Drop Table».
ох уж этот бородатый юмор))
Рэйчел пояснила, что связывалась множество раз с техподдержкой Apple, но специалисты компании никак не могли ей помочь в данной ситуации, хотя оплата за выбранный ранее тариф в iCloud снимается с банковской карточки ежемесячно, а ее учетная запись не заблокирована.
Интересно, а почему она связывалась с представителями Apple, а не с представителями суда? Деньги снимают, но услугу не оказывают. Ладно у нас судиться не любят и не хотят, а там-то… Глядишь, после иска сразу и баг бы нашли, и починили бы в момент.
У Apple много очень-очень хороших юристов, и хитрые лицензионные соглашения, написанные этими хорошими юристами. Так что это реально лотерея, и в случае проигрыша судебные издержки останутся на истце.
Интересно, а почему она связывалась с представителями Apple, а не с представителями суда?
Потому что в суде Вас спросят связывались-ли Вы с техподдержкой для устранения ошибки и возврата денег за не оказанную услугу.
На всякие суды уйдёт куча времени и нервов, а выхлоп будет пара десятков баксов, которые она до этого заплатила. Хотя там всё решится до суда выплатой компенсации. Но время всё равно потратишь. Проще связаться с ними.
Эппл иногда реально пугает. С одной стороны, фокус на безопасность и конфеденциальность, с другой — такие детские ошибки.
Да, ошибка реально детская. Что за индусы там код ваяли?
createFromResponse
класса AuthenticatedUserDetails
, который собственно, создает объект из данных реквеста, копируя из него определенные поля, указанные в f и m (dsid, lastname, firstname, и т.п.). И возращает этот новый объект.Собственно все поля успешно присваиваются полем, кроме особого случая, когда эта поле равно строке
'true'
(тогда присвоить true)."true"
строго сравнивается со строкой "True"
. Вроде, никакого .toLowerCase()
там нет… Похоже, что ошибка не в этом месте.Type error: cannot set value ‘true’ to property ‘lastName‘
Вот совсем не очевидно — буквально вчера очепятался в yml для пайплайна гитлаба, вместо reports:junit: написал reports:jUnit:. В ошибке получил "неопознанный ключ 'junit'"
Это показывает то, что поддержка в Эппл находится в таком состоянии, что любая нестандартная незаскриптованная проблема, особенно единичная просто не может найти свой путь к разработчикам. И это проблема не только Эппл.
Думаешь что в другой линии сидят люди с высокой квалификацией?) Или вопросы сильно отличаются?)А какой иначе в них смысл? Квалификация каждой линии должна быть достаточной, чтобы решать, передавать ли вопрос в следующую линию.
Ещё раз ни один нормальный инженер не будет работать на поддержке в любом её виде. Если такой попадается это скорее исключение чем правило. А не инженер будет подобные вопросы игнорить и заруливать на шаблоны ответов. При этом совершенно искренне не понимая «А что он докопался»Инженер не должен «сидеть в техподдержке» в смысле разговора с пользователями. Задача техподдержки — оформить багрепорт для решения проблемы инженером. И не знаю, где и кем вы работали, но всю мою карьеру разработчика мне регулярно приходится заниматься багами, заведёнными техподдержкой по факту обращений пользователей.
t[o] = "true"
...?
Если яблочники этого не понимают то это очень печально.
Напомнило фильм Идиократия, где одному парню присвоили имя You Sure .
Обладательница фамилии True полгода не может воспользоваться своим аккаунтом в Apple iCloud