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 заявление в отличие от выражения.

Другие вопросы по тегам