F# Условные выражения if...then..else, возвращающие единицу или ()
Условные выражения F# требуют проверки условия, ветвь для true и ветвь для false. Например:
let x =
if ("hello" = null)
then true
else false //error if else branch missing
Тем не менее, что-то становится странным, когда unit
ака ()
, вовлечен.
let y =
if ("hello" = null)
then raise <| new ArgumentNullException()
else () //happy with or without else branch
И проще:
let z =
if ("hello" = null)
then ()
else () //happy with or without else branch
Почему неelse
ветвь требуется, когда unit
возвращается?
4 ответа
Рассмотрим следующий код:
let f a = if a > 5 then true
Если вы позвоните f 10
, это возвращает true
,
Теперь спросите себя: что должно f 2
вернуть? Я знаю, что ты собираешься сказать false
, но как это узнает компилятор? Я имею в виду, это так же вероятно, что вы хотели вернуть true
в обоих случаях, не так ли? Или даже, возможно, сбой в a <= 5
случай, кто знает?
Таким образом, чтобы программа была "завершена" (т.е. содержала инструкции о том, что делать в любой ситуации), вы всегда должны указывать else
ветка.
unit
Впрочем, особенный.
возврате unit
означает, что нет значимого возвращаемого значения. по существу unit
означает побочный эффект: это означает, что вещь, которая возвратила это, должна была произвести некоторый эффект во внешнем мире. Поскольку F# не является чистым языком, такой unit
возвращающиеся вещи довольно вездесущи. Например, ведение журнала отладки:
let f x =
if x < 42 then printfn "Something fishy, x = %d" x
x + 5
С такими заявлениями нет никакой двусмысленности: всегда известно, что else
ветка предназначена для возвращения ()
также. Ведь других ценностей нет unit
, здесь? В то же время, всегда добавляя else ()
в конце было бы очень утомительно и запутанно. Таким образом, в интересах удобства использования, компилятор не требует else
ветвь в этом конкретном случае.
В F# if
это выражение, а не утверждение. Каждое выражение должно возвращать значение. И оба if
а также else
необходимо вернуть значение того же типа, потому что язык F# строго типизирован. Так что если нет else
тогда ветка по умолчанию имеет тип unit
, но если ваш if
возвращает значение с типом, отличным отunit
тогда вам нужно иметь else
с тем же типом.
На вопрос уже дан ответ, поэтому я просто продолжу свои собственные поверхностные наблюдения
В F# у нас нет операторов и выражений. В F# мы составляем вычисления, используя только выражения. Это хорошая вещь.
В C# где if
это утверждение имеет смысл
if(x)
{
return 1;
}
Если x
Значение true, выполнение останавливается, и мы возвращаем вызывающей стороне результат: 1
,
В F# где if
выражение это означает, что if
должен производить значение независимо от того, какая ветвь была взята. Поэтому это имеет мало смысла
if x then 1 // What value should else branch evaluate to?
Мы должны указать обе ветви
if x then 1 else 0
Как показывают другие ответы, есть особый случай, когда else
опущен if
выражение произведет unit
значение, самое близкое, что мы получаем к заявлению в F#. Потому что тип if
выражение является единицей, "истинная" ветвь должна иметь тип unit
,
if x then () // This works
if x then () else () // This works
if x then 1 // This won't work, type mismatch
Типы значений, создаваемых в каждой ветви, должны совпадать. Если нет явной ветви else, ее тип является единицей. Следовательно, если тип ветви тогда любой, кроме unit, должна быть ветвь else с тем же типом возврата.
Если вы удалите else
из первого фрагмента, это эквивалентно
let x =
if ("hello" = null)
then true
else ()
который не проверяет тип.
Зачем? Я бы предположил для совместимости с OCaml.
else expr3
часть может быть опущена, в этом случае по умолчаниюelse ()
, (7.7.2)
Вы можете думать о unit
-returning if
как эквивалент C-подобного if
заявление в отличие от выражения.