При создании клиента для веб-API в C # я столкнулся с проблемой, связанной null
со значением, в которой он представлял бы две разные вещи:
- ничего , например,
foo
может иметь или не иметьbar
- неизвестно : по умолчанию ответ API включает только подмножество свойств, вы должны указать, какие дополнительные свойства вы хотите. Таким образом, неизвестно означает, что свойство не было запрошено у API.
После некоторых поисков я узнал о типе Maybe (или Option), о том, как он используется в функциональных языках и как он «решает» проблемы нулевой разыменования, заставляя пользователя задуматься о возможном отсутствии значения. Тем не менее, все ресурсы, с которыми я столкнулся, говорили о замене null на Maybe . Я нашел некоторые упоминания о трехзначной логике , но я не до конца понимаю ее, и в большинстве случаев ее упоминание было в контексте «это плохо».
Теперь мне интересно, имеет ли смысл иметь как концепцию null, так и Maybe , чтобы представлять неизвестное и ничего соответственно. Это трехзначная логика, о которой я читал, или у нее другое имя? Или это намеченный способ вложить «Возможно в»?
null
. Это полностью сломанная идея.M M x
иM x
должны иметь ту же семантику.Maybe a
то же самое, что , семантически, то же самое, что , и изоморфно типу из ответа @Andej. Для этого типа вы также можете определить свой собственный экземпляр монады и, следовательно, использовать разные комбинаторы монад. + 1 + 1Maybe Maybe a
UserInput a
M (M x)
иM x
должно иметь одинаковую семантику». ВозьмемM = List
для примера: списки списков не то же самое, что списки. КогдаM
монада, есть преобразование (а именно монада умножение) отM (M x)
кM x
которой объясняет отношения между ними, но они не имеют «такую же семантику».Ответы:
null
Значение в настоящее время по умолчанию везде только действительно сломана идея , так что забудьте об этом.У вас всегда должна быть именно та концепция, которая лучше всего описывает ваши фактические данные. Если вам нужен тип, который указывает «неизвестно», «ничего» и «значение», вы должны иметь именно это. Но если это не соответствует вашим реальным потребностям, вы не должны этого делать. Это хорошая идея, чтобы увидеть, что другие люди используют и что они предложили, но вам не нужно слепо следовать им.
Люди, которые разрабатывают стандартные библиотеки, пытаются угадать общие шаблоны использования, и они обычно делают это хорошо, но если вам нужна конкретная вещь, вы должны определить ее самостоятельно. Например, в Haskell вы можете определить:
Может даже случиться, что вам следует использовать что-то более наглядное, что легче запомнить:
Вы можете определить 10 таких типов, которые будут использоваться для различных сценариев. Недостатком является то, что у вас не будет доступных ранее существующих библиотечных функций (например, функций
Maybe
), но это обычно оказывается деталями. Это не так сложно, чтобы добавить свой собственный функционал.источник
Насколько я знаю, значение
null
в C # является возможным значением для некоторых переменных, в зависимости от его типа (я прав?). Например, экземпляры какого-то класса. Для остальных типов (напримерint
,bool
и т. Д.) Вы можете добавить это значение исключения, объявив переменные с помощьюint?
илиbool?
вместо (это именно то, чтоMaybe
делает конструктор, как я опишу далее).Maybe
Конструктор типа от функционального программирования добавляет это новое значение исключения для данного типа данных. Так что, еслиInt
это тип целых чисел илиGame
тип состояния игры, тоMaybe Int
есть все целые числа плюс нуль (иногда называют ничего значение). То же самое дляMaybe Game
. Здесь нет типов , которые приходят сnull
значением. Вы добавляете это, когда вам это нужно.ИМО, этот последний подход лучше для любого языка программирования.
источник
Maybe
s иnull
полностью отбросить ?isinstance
испытаниях. Scala также имеет «правильное» сопоставление с образцом, и C♯ фактически недавно также получил простые шаблоны.final
(для класса, который не может быть унаследован от), он также имеетsealed
для класс, который может быть расширен только внутри одной и той же единицы компиляции . Это означает, что компилятор может статически знать все возможные подклассы (при условии, что все они либо сами,sealed
либоfinal
) и, таким образом, выполнять исчерпывающие проверки на соответствие шаблонам, что статически невозможно для языка с загрузкой кода времени выполнения и неограниченным наследованием.int??
является незаконным в C #.Если вы можете определить тип объединения, как
X or Y
, и если у вас есть тип имени ,Null
который представляет толькоnull
значение (и ничего больше), то для любого типаT
,T or Null
на самом деле не так уж отличается отMaybe T
. Единственная проблемаnull
заключается в том, что язык обрабатывает типT
какT or Null
неявный, создаваяnull
допустимое значение для каждого типа (кроме примитивных типов в языках, в которых они есть). Это то, что происходит, например, в Java, гдеString
также принимает функция, которая принимаетnull
.Если вы рассматриваете типы как набор значений и
or
как объединение наборов, то(T or Null) or Null
представляет набор значенийT ∪ {null} ∪ {null}
, который совпадает сT or Null
. Есть компиляторы, которые выполняют этот вид анализа (SBCL). Как сказано в комментариях, вы не можете легко различатьMaybe T
иMaybe Maybe T
когда вы просматриваете типы как домены (с другой стороны, это представление весьма полезно для программ с динамической типизацией). Но вы также можете оставить типы в качестве алгебраических выражений, где(T or Null) or Null
представляет несколько уровней Maybes.источник
Maybe (Maybe X)
это не то же самое, чтоMaybe X
.Nothing
это не то же самое, чтоJust Nothing
. СопоставлениеMaybe (Maybe X)
с нимMaybe X
делает невозможнымMaybe _
полиморфное лечение .Вам нужно выяснить, что вы хотите выразить, а затем найти способ, как выразить это.
Во-первых, у вас есть значения в проблемной области (например, числа, строки, записи клиентов, логические значения и т. Д.). Во-вторых, у вас есть несколько дополнительных общих значений поверх значений проблемной области: например, «ничего» (известное отсутствие значения), «неизвестно» (нет сведений о наличии или отсутствии значения), не упоминалось «» что-то "(определенно есть значение, но мы не знаем, какое именно). Может быть, вы можете думать о других ситуациях.
Если вы хотите иметь возможность представлять все значения плюс эти три дополнительных значения, вы должны создать перечисление со словами «ничто», «неизвестно», «что-то» и «значение» и перейти оттуда.
В некоторых языках некоторые случаи проще. Например, в Swift у вас есть для каждого типа T тип «необязательный T», который имеет в качестве возможных значений nil плюс все значения T. Это позволяет вам легко справляться со многими ситуациями, и идеально, если у вас есть «ничто» и « значение". Вы можете использовать его, если у вас есть «неизвестно» и «значение». Вы не можете использовать его, если вам нужно обрабатывать «ничего», «неизвестно» и «значение». Другие языки могут использовать «ноль» или «возможно», но это просто другое слово.
В JSON у вас есть словари с парами ключ / значение. Здесь ключ может просто отсутствовать в словаре или может иметь значение null. Таким образом, тщательно описав, как вещи хранятся, вы можете представить общие значения плюс два дополнительных значения.
источник