Why do I need to use the unit type in F# if it supports the void type?
Asked Answered
G

4

15

I read this MSDN article:

Unit Type (F#)

...The unit type is a type that indicates the absence of a specific value; the unit type has only a single value, which acts as a placeholder when no other value exists or is needed ... The unit type resembles the void type in languages such as C# and C++...

So... Alright, I understand, that the unit type is such a type, which has only a single value (). But I have some questions:

  1. Why is it needed?
  2. When is it needed?

I don't understand why not to use the void type in F#, like C# and C++ use.

If I look at the following table:

Primitive Types (F#)

Type   .NET Type    Description  
void   Void         Indicates no type or value.

I see that F# does have a void type. So I don't understand why the unit type needed; it looks like it is very similar to void.

I suppose that it relates to the functional language paradigm and that's why it's needed, so please... explain more about this to me.

Gallfly answered 20/12, 2013 at 13:37 Comment(5)
See: What does this '()' notation mean?Cavil
I've read you answer to other question, it's great :) but I have a new question you. If I use the next code: let b () = System.DateTime.Now;;System.Console.WriteLine(b);;, why is it returning Program+clo@16 in console output and what's it?Gallfly
See this and thisSeamanship
Because b is bound to a function. You can call it with b()Seamanship
It should be System.Console.Writeline(b());;Cavil
V
15

In C#, there is no value of type void that can be used as an argument type. Furthermore, void can't be used as a generic type argument (so for example C# needs to use parallel Func<...> and Action<...> delegate types, but F# needs only a single function type ... -> ... which can abstract over both). This ends up greatly simplifying F# programming in many cases; for example, an Async action which performs some side effects but doesn't return a value is an instance of type Async<unit>, but in C# there's no way to create a corresponding Task<void> or whatever.

Vagarious answered 20/12, 2013 at 14:4 Comment(0)
C
8

See Unit Type from Wikipedia

Void type as unit type

In C, C++, C#, and Java, void expresses the empty type. The unit type in C would be struct {}, but an empty struct is forbidden by the C language specification. Instead void is used in a manner that simulates some, but not all, of the properties of the unit type, as detailed below.

Difference in calling convention

The first notable difference between a true unit type and the void type is that the unit type may always be the type of the argument to a function, but the void type cannot be the type of an argument in C, despite the fact that it may appear as the sole argument in the list.

Difference in storage

The second notable difference is that the void type, being empty, can never be stored in a record type, i.e. in a struct or a class in C/C++. In contrast, the unit type can be stored in records in functional programming languages, i.e. it can appear as the type of a field; the above implementation of the unit type in C++ can also be stored. While this may seem a useless feature, it does allow one for instance to elegantly implement a set as a map to the unit type; in the absence of a unit type, one can still implement a set this way by storing some dummy value of another type for each key.

Cavil answered 20/12, 2013 at 14:17 Comment(0)
O
7

Another way to look at it is to visualize () as a tuple with arity of 0.

Given that () is used to delimit a tuple, we get

(abc, def, xyx) a tuple with arity of 3
(abc, def) a tuple with arity of 2
(abc) a tuple with arity of 1, which can be reduced to abc
() a tuple with arity of 0, called unit

In functional languages based on the lambda calculus, a function takes a single parameter and returns a single value. Multiple parameters are supported by currying. Parameters and return values can also be tuples to support multiple values.

My interpretation of unit / (), is no values, expressed as a tuple.

Oust answered 20/12, 2013 at 16:38 Comment(0)
H
1

This article explains some limitations of void and why you even might want to have Unit in C#: https://chtenb.dev/?page=unit-cs

The bottom line is that you can't use void as a type parameter in generic types, like Task<void>. Using a unit type instead of void solves this problem.

This is the Unit implementation used in the article.

public struct Unit : IEquatable<Unit>
{
  public static readonly Unit unit;
  public override bool Equals(object obj) => obj is Unit;
  public override int GetHashCode() => 0;
  public static bool operator ==(Unit left, Unit right) => left.Equals(right);
  public static bool operator !=(Unit left, Unit right) => !(left == right);
  public bool Equals(Unit other) => true;
  public override string ToString() => "()";
}
Hugohugon answered 17/7, 2021 at 10:8 Comment(5)
Please do not post link-only answers. You should put all of the salient details in the answer itself and only link to support your content.Subjunctive
I know that the article defines Unit.unit because it can't do Unit.Unit, but I'd suggest Unit.Default instead to stick with the normal C# naming convention. Also, adding public override string ToString() => "()"; would be a nice touch.Subjunctive
Microsoft's Reactive Framework also has an implementation - I'd trust that one. Here it is: github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/…Subjunctive
The reason I chose unit is so you can do using static Unit; and then use unit everywhere as if it where a builtin type. Also because it's brief. I did not know the reactive framework had a similar one.Hugohugon
What would've been nice is if () was valid syntax for the empty valuetuple. We could then setup implicit conversion operators to make it a unit automatically. You could also use default to refer to unit btw.Hugohugon

© 2022 - 2024 — McMap. All rights reserved.