I’ve been learning F#, and I’m writing a tool in it. Everything works fine except when I need to handle invalid input or exceptional situations.
What is the recommended way of dealing this in F#? The way my functions are constructed and used I’m not sure how to send back a message that tells the calling function that the call failed due to bad input and something similar to C#’s loop continue needs to happen.
I’m suffering due to the loss of Return. Can I return from an F# function anywhere in it, or does it have to be on the last line?
Here is an example of some of my code where the input is in some cases essentially empty, and I am detecting that. I need to prevent the steps in the process that follow from running.
let SchemaQueryString s =
let sqsValid s =
let Strcat (x : string) (y : string) = x + ", " + y;
let RecFieldName (x : MeasurementRecord) = x.FieldName;
let mapr = List.map RecFieldName s.MeasurementTypes;
let cores = List.reduce Strcat mapr;
"SELECT ProductionCode, TestTime, " + cores + " FROM " + s.TemplateName + " ORDER BY ProductionCode, TestTime";
match s.MeasurementTypes.Length with
| 0 -> ""
| _ -> sqsValid s;
let TemplateMigration tcs rr =
let sql = SchemaQueryString tcs;
let sqlc = new SqlCommand(sql, QCOld);
let sqldr = sqlc.ExecuteReader();
ignore (sqldr.Read());
...
I need to abort the TemplateMigration function and return immediately on a match of the sql variable with “”. Or construct some kind of union type that indicates that no sql query string could be constructed because for a specific case it couldn’t be done.
Thanks for your help. I’m gradually figuring out this language, and I like a lot about it, but I’m struggling with some aspects of it where I don’t know how to replace my procedural ideas about how to do something with functional ideas.
When using functional languages for the first time many people trip up on the idea that everything is an expression. This means that control flow is performed using control flow expressions.
So what’s the difference between a statement and an expression?
A statement has no value, i.e. it’s not an instance of a type. An expression has a value (even if that value is of the special type
unitwhich has a single instance()that represents no value.)In a procedural language we use statements to control the flow of execution so:
Here we define a variable
strof typestringto have a valuenull. Then we either ignore it andreturnif it’s Monday, assignargs[1]if available or"Hello"otherwise and print it.When the control flow statement is a few lines long this sort of programming is fairly safe. However as soon as the statement becomes greater than a page long, or complex and nested, we become more likely to introduce errors, typically the billion dollar mistake.
Pure functional languages use control flow expressions instead. This means that an
ifexpression has a value! Let’s slowly implement the control in the former code example. First lets tackle the assigning the stringstr.Here we see that we are directly binding the value of the
ifexpression tostr. Both branches (sub-expressions) of theifexpression must evaluate to an instance of typestring. We simply can not forget to assign a value to string because the compiler will throw an error should we try.But it would seem that there are many cases when we wish to do different types of things in different branches of our control flow. In our example if it’s Monday we don’t want to do anything. This is where the power of discriminated unions comes into play. These allow us to combine heterogeneous types into a whole, preserving the power of expression-based languages to type-check our code at every level.
In our case we do not need to create a new union type as the
optiontype already encompasses this functionality. We thus rewrite so:Now the value we return from our
ifexpression isstring option(orOption<string>in C#) and we then use pattern matching to safely deal with all possible results of our control flow.As you are returning the
unitvalue fromTemplateMigrationyou could simple return the same value from both branches of anifexpression so:However it would probably be better to return an
optionvalue fromSchemaQueryStringto ‘propagate’ strongly typed values throughout your code.