Размышлял на досуге, зачём введён тип unit в F#...
Причём это не просто синоним void, а обычный класс Microsoft.FSharp.Core.Unit, да ещё и с int _dummy внутри
Хотя unit никогда не создаётся, всегда возвращается адресный null...
Должен же быть смысл, какую-нибудь проблему это решает
Я понимаю, что в ФП функции обязаны возвращать что-либо, но зачем вводить отдельный тип, если можно
| 1 -> ()
за сценой превращать в:
case 1: return;
Как то читал Эрика Липперта про
вариантность void, подумал по началу, что unit — просто уловка, вариантный аналог void'а. Проверил — это не так...
Пока писал этот пост пришло в голову, что может unit нужен для поддержки функциональных типов, всяким FastFunc<T1,T2> нельзя же указать System.Void в качестве generic-параметра...
Я правильно понимаю предназначение unit?
22.07.09 00:56: Перенесено из '.NET'
Здравствуйте, Пельмешко, Вы писали:
П>Размышлял на досуге, зачём введён тип unit в F#...
П>Причём это не просто синоним void, а обычный класс Microsoft.FSharp.Core.Unit, да ещё и с int _dummy внутри
П>Хотя unit никогда не создаётся, всегда возвращается адресный null...
Я этот вопрос себе тоже задавал, пришел к следующим выводам:
Нужен был просто именно значение некоторого типа, которое никому не интересно. Т.е. само значение есть, но что именно там — не важно. И это как раз отличает его от void-а, который есть "ничто", т.е. даже значения его нет.
П>Должен же быть смысл, какую-нибудь проблему это решает
Конечно решает!
Имея такой тип, можно комбинировать в штатном порядке функции, принимающие и возвращающие unit. С void-ом такое не прокатит.
П>Я понимаю, что в ФП функции обязаны возвращать что-либо, но зачем вводить отдельный тип, если можно
П>| 1 -> ()
за сценой превращать в:
П>case 1: return;
нет, void нас не устраивает, т.к. функция должна возвращать хоть что-нибудь.
П>Как то читал Эрика Липперта про вариантность void, подумал по началу, что unit — просто уловка, вариантный аналог void'а. Проверил — это не так...
именно к вариантности не имеет отношения, как мне кажется.
П>Пока писал этот пост пришло в голову, что может unit нужен для поддержки функциональных типов, всяким FastFunc<T1,T2> нельзя же указать System.Void в качестве generic-параметра...
А вот это — оно самое. Здесь нам не надо подразделять на Func<T,...> и Action<T,...>. Всегда обходимся одним способом.
П>Я правильно понимаю предназначение unit?
Вобщем — да. Это заменитель void, но более гибкий.
Правда есть один трюк, который я не до конца понимаю.
> ignore;;
val it : ('a -> unit) = <fun:clo@0>
при том, что сигнатура метода ignore в рефлекторе определена как
public static void ignore<T>(T value)
Т.е. void интерпретируется как и unit.
Но не совсем:
> let f1 () = ()
- let f2 () = ()
- let f3 = f1 >> f2;;
val f1 : unit -> unit
val f2 : unit -> unit
val f3 : (unit -> unit)
объявленные так функции мы можем комбинировать.
и так тоже:
> let f4 = f1 >> ignore;;
val f4 : (unit -> unit)
а так уже нельзя:
> let f5 = ignore >> f1;;
let f5 = ignore >> f1;;
----^^
stdin(15,5): error FS0030: Value restriction. The value 'f5' has been inferred to have generic type
val f5 : ('_a -> unit)
Either make the arguments to 'f5' explicit or, if you do not intend for it to be generic, add a type annotation.
>
Здравствуйте, Пельмешко, Вы писали:
П>Должен же быть смысл, какую-нибудь проблему это решает
void нельзя использовать как параметр дженерик-типа. Самодельный unit решает эту задачу. Такой тип есть в
Common Generics Library:
The Unit type is a filler type. This is useful, for example, when an existing generic type is used, but one of its type arguments is not required for the particular application.
// The single-valued type.
[Serializable]
public enum Unit { Unit };
Я использую такой:
[Serializable]
public struct Unit : IEquatable<Unit>
{
#region Fields
private const int HashCodeMagicNumber = 67981;
#endregion Fields
#region Properties
public static Unit Value {
[DebuggerStepThrough]
get { return default(Unit); }
}
#endregion Properties
#region Overrides
public override bool Equals(object obj) {
return obj is Unit;
}
public override int GetHashCode() {
return HashCodeMagicNumber;
}
#endregion Overrides
#region IEquatable<Unit> Members
public bool Equals(Unit other) {
return true;
}
#endregion IEquatable<Unit> Members
#region Operators
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "left", Justification = "We are can not change the operator signature.")]
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "right", Justification = "We are can not change the operator signature.")]
public static bool operator ==(Unit left, Unit right) {
return true;
}
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "left", Justification = "We are can not change the operator signature.")]
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "right", Justification = "We are can not change the operator signature.")]
public static bool operator !=(Unit left, Unit right) {
return false;
}
#endregion Operators
}
Здравствуйте, samius, Вы писали:
S>Т.е. void интерпретируется как и unit.
S>Но не совсем:
S>>> let f1 () = ()
S>- let f2 () = ()
S>- let f3 = f1 >> f2;;
S>val f1 : unit -> unit
S>val f2 : unit -> unit
S>val f3 : (unit -> unit)
S>
S>объявленные так функции мы можем комбинировать.
S>и так тоже:
S>>> let f4 = f1 >> ignore;;
S>val f4 : (unit -> unit)
S>
S>а так уже нельзя:
S>>> let f5 = ignore >> f1;;
S> let f5 = ignore >> f1;;
S> ----^^
S>stdin(15,5): error FS0030: Value restriction. The value 'f5' has been inferred to have generic type
S> val f5 : ('_a -> unit)
S>Either make the arguments to 'f5' explicit or, if you do not intend for it to be generic, add a type annotation.
>>
S>
Value restriction не имеет отношения к unit. Automatic generalization применяется к функциям и простым типам данных. Функции, определенные как значения автоматически не обобщаются.
> let a x = 5;;
val a : 'a -> int
> let b x = x + 1;;
val b : int -> int
> let c = a >> b;;
let c = a >> b;;
----^
stdin(22,5): error FS0030: Value restriction. The value 'c' has been inferred to have generic type
val c : ('_a -> int)
Either make the arguments to 'c' explicit or, if you do not intend for it to be generic, add a type annotation.
простой фикс — явно задать аргументы
> let c x = (a >> b) x;;
val c : 'a -> int
Здравствуйте, desco, Вы писали:
D>Здравствуйте, samius, Вы писали:
S>>а так уже нельзя:
S>>>>> let f5 = ignore >> f1;;
S>> let f5 = ignore >> f1;;
S>> ----^^
S>>stdin(15,5): error FS0030: Value restriction. The value 'f5' has been inferred to have generic type
S>> val f5 : ('_a -> unit)
S>>Either make the arguments to 'f5' explicit or, if you do not intend for it to be generic, add a type annotation.
>>>
S>>
D>Value restriction не имеет отношения к unit. Automatic generalization применяется к функциям и простым типам данных. Функции, определенные как значения автоматически не обобщаются.
Что-то я тут лопухнул. Спасибо.
Здравствуйте, Пельмешко, Вы писали:
П>Размышлял на досуге, зачём введён тип unit в F#...
А он там и не введен, а взят из прородителя OCaml'a
П>Причём это не просто синоним void, а обычный класс Microsoft.FSharp.Core.Unit, да ещё и с int _dummy внутри
П>Хотя unit никогда не создаётся, всегда возвращается адресный null...
а void как раз введен, его в OСaml нет.