An important point that hasn't been mentioned so far is the difference between if .. then .. else
and if .. then
without the else
branch.
If
in functional languages
The functional interpretation of if
is that it is an expression that evaluates to some value. To evaluate the value of if c then e1 else e2
you evaluate the condition c
and then evaluate either e1
or e2
, depending on the condition. This gives you the result of the if .. then .. else
.
If you have just if c then e
, then you don't know what the result of the evaluation should be if c
is false
, because there is no else
branch! The following clearly does not make sense:
let num = if input > 0 then 10
In F#, expressions that have side-effects like printf "hi"
return a special value of type unit
. The type has only a single value (written as ()
) and so you can write if
which does an effect in just a single case:
let u = if input > 0 then printf "hi" else ()
This always evaluates to unit
, but in the true
branch, it also performs the side-effect. In the false
branch, it just returns a unit
value. In F#, you don't have to write the else ()
bit by hand, but conceptually, it is still there. You can write:
let u = if input > 0 then printfn "hi"
Regarding your additional example
The code looks perfectly fine to me. When you have to deal with API that is imperative (like lots of the .NET libraries), then the best option is to use the imperative features like if
with a unit
-returning branch.
You can use various tweaks, like represent your data using option<string>
(instead of just string
with null
or empty string). That way, you can use None
to represent missing data and anything else would be valid input. Then you can use some higher-order functions for working with options, such as Option.iter
, which calls a given function if there is a value:
maybeData |> Option.iter (fun data ->
let byteData = System.Text.Encoding.Unicode.GetBytes(data)
req.ContentLength <- int64 byteData.Length
use postStream = req.GetRequestStream()
postStream.Write(byteData, 0, byteData.Length) )
This is not really less imperative, but it is more declarative, because you don't have to write the if
yourself. BTW: I also recommend using use
if you want to Dispose
object auotmatically.