I'm wondering about the two syntax for the getters:
type Direction =
| Undefined
| Buy
| Sell
member this.Symbol =
match this with
| Direction.Buy -> '↑'
| Direction.Sell -> '↓'
| Direction.Undefined -> '→'
member this.Symbol2 with get() =
match this with
| Direction.Buy -> '↑'
| Direction.Sell -> '↓'
| Direction.Undefined -> '→'
Is there any practical difference between the first one and the second one?
The IL looks identical, but from the usage standpoint? is one preferred for any reason?
They are identical, yes. In general, prefer the first one. The reason why the second one exists is if you have some special logic to do that isn't just grabbing a value. It's more flexible, but more syntax.
Related
I came across an oddity in the F# pattern matching syntax today, which can lead to apparent failures in the exhaustivity check.
type Thing =
| This
| That
| Other
let useThing =
function
| This -> "A"
| That -> "A"
| That -> "B" // compiler complains
| Other -> "B"
In the above scenario the compiler helpfully tells me that the second That rule will never be matched. However, if I had tried to make the code a bit more compact and had written
let useThing =
function
| This | That -> "A"
| That | Other -> "B"
I do not get any help from the compiler. I think the reason is that | This | That ->. "A" is not a shortcut for | This -> "A" | That -> "A", even though it looks very much like it is (and I've seen many code samples that treat it as such). Instead, from what I can find, the pipe symbol is used both to separate individual patterns, and also as OR pattern.
This is not a big issue for most DUs, but I encountered the problem when mapping a DU with a large number of cases into another DU with a small number of cases. My attempt to use the shortcut syntax caused a bug.
So my questions are:
Is my interpretation correct?
Is there any workaround apart from listing each pattern on a separate line?
Your interpretation is correct.
By leaving out the actions for the first This and second That you are creating an OR pattern as described in Pattern Matching (F#)
To me this is slightly confusing, too, since the logical 'or' is || in F#.
And while it is easy to see the first bar as new alternative and second bar as or in your formatting it becomes less obvious in
let useThing =
function
| This
| That -> "A"
| That
| Other -> "B"
However the compiler can tell whether a whole pattern is useless but it cannot simplify a pattern.
That | Other has a valid match and is therefore not redundant as considered by the compiler.
You can think of much more involved patterns where it would be not at all clear if parts can be left out or how to simplify them.
I am a new programmer in general, and as well to F#. I've ran into this particular problem several times, and have yet to solve it efficiently in my opinion. Here is the problem:
I have these example types:
type Retail1 = | Fashion | Auto | Sports
type Wholesale1 = | Fashion | Auto | Sports
type Events1 = | Wedding | Birthday
type Product =
| Retail of Retail1 | Wholesale of Wholesale1 | Events of Events1
| NoProduct
I want to convert the possibility of the first three types to the Product type via a function:
let convertToProduct (retail: Retail1 option)
(wholesale: Wholesale1 option) (events: Events1 option) =
// convert to Product here
if retail.IsSome then Retail retail
elif wholesale.IsSome then Wholsale wholseale
elif events.IsSome then Events events
else NoProduct
The way that I have handled it in the pass is just to chain a long if elif statement together to check for each condition and return the final type of Product, but this does not feel correct, or at the very least idiomatic to F#. What would be the recommended approach to this problem?
How about something like this:
let convertToProduct (retail: Retail1 option) (wholesale: Wholesale1 option) (events: Events1 option) =
match (retail, wholesale, events) with
|Some rt, None, None -> Retail rt
|None, Some wh, None -> Wholesale wh
|None, None, Some ev -> Events ev
|_ -> NoProduct
This exploits the fact that if you convert all the arguments into a tuple, you can do pretty concise pattern matching on the result.
Pattern matching is actually extremely powerful, you can find more details about the types of pattern matching you can perform in the MSDN documentation.
Is there any way for me to fully qualify a discriminated union path?
I currently have this:
type PostionOfScott =
| ScottOnFirst
| ScottOnSecond
| ScottOnThird
type PostionOfBrian =
| BrianOnFirst
| BrianOnSecond
| BrianOnThird
type PostionOfCherice =
| ChericeOnFirst
| ChericeOnSecond
| ChericeOnThird
I would like to do this:
type PostionOfScott =
| First
| Second
| Third
type PostionOfBrian =
| First
| Second
| Third
type PostionOfCherice =
| First
| Second
| Third
However, when I provide the following code:
(*Functions*)
let hit play batterUp =
match batterUp with
| ScottAtBat -> match play with
| Single -> Scott First
| Double -> Scott Second
| Tripple -> Scott Third
I receive the following error:
Error This expression was expected to have type
PostionOfScott but here has type
PostionOfCherice
I understand that the last declared discriminated union type will be referenced if there's any ambiguity.
However, is there any way for me to fully qualify a discriminated union path?
Hence, I want to reduce verbiage within my union cases.
I think that repeating the position type definition for each player is probably not the best design choice. It would make more sense to keep these things separate and then your problem goes away naturally.
type Position =
| First
| Second
| Third
type Player =
| Scott
| Brian
| Cherice
type PlayerPosition = {Player : Player; Position : Position}
Then you can pattern match:
let hit play batterUp =
match batterUp with
|{Player = Scott; Position = First} -> ...
Sounds like you're looking for RequireQualifiedAccess attribute.
[<RequireQualifiedAccess>]
type PositionOfScott =
| First
| Second
| Third
With that, you'll need to use PositionOfScott.First instead of First outside of PositionOfScott members. Very useful for unions with generally nondescript values like Yes or Any.
To check if a variable a has type T, I can use
if (a :? T)
But what if T is a universally-defined and has several subtypes T1,T2,T3 in which I'm only interested in checking if a has type T1? For example:
type Uni = Iu of int
| Flu of float
| Su of string
| Bu of bool
| Lu of Uni List
| Fu of (Uni -> Uni)
How can I check if a variable aString has type Su?
Help is appreciated.
You can use pattern matching with the underscore (wildcard) pattern to ignore the float value:
let isSu = function Su _ -> true | _ -> false
Although you can think of discriminated unions like Uni as class hierarchies with some base class and a number of subtypes, I do not think this is very helpful in this case. It is much better to think of them as types which can have one of several possible representations. So instead of "checking for a subtype" you are really just checking if a value is represented using the Su case.
Have a look at this F#/OCaml code:
type AllPossible =
| A of int
| B of int*int
| ...
| Z of ...
let foo x =
....
match x with
| A(value) | B(value,_) -> (* LINE 1 *)
(* do something with the first (or only, in the case of A) value *)
...
(* now do something that is different in the case of B *)
let possibleData =
match x with
| A(a) -> bar1(a)
| B(a,b) -> bar2(a+b)
| _ -> raise Exception (* the problem - read below *)
(* work with possibleData *)
...
| Z -> ...
So what is the problem?
In function foo, we pattern match against a big list of types.
Some of the types share functionality - e.g. they have common
work to do, so we use "|A | B ->" in LINE 1, above.
We read the only integer (in the case of A), or the first integer
(in the case of B) and do something with it.
Next, we want to do something that is completely different, depending
on whether we work on A or B (i.e. call bar1 or bar2).
We now have to pattern match again, and here's the problem: In this
nested pattern match, unless we add a 'catchAll' rule (i.e. '_'),
the compiler complains that we are missing cases - i.e. it doesn't
take into account that only A and B can happen here.
But if we add the catchAll rule, then we have a far worse problem:
if at some point we add more types in the list of LINE1
(i.e. in the line '|A | B ->' ... then the compiler will NOT help
us in the nested match - the '_' will catch them, and a bug will
be detected at RUNTIME. One of the most important powers of
pattern matching - i.e. detecting such errors at compile-time - is lost.
Is there a better way to write this kind of code, without having
to repeat whatever work is shared amongst A and B in two separate
rules for A and B? (or putting the A-and-B common work in a function
solely created for the purpose of "local code sharing" between A and B?)
EDIT: Note that one could argue that the F# compiler's behaviour is buggy in this case -
it should be able to detect that there's no need for matching beyond A and B
in the nested match.
If the datatype is set in stone - I would also prefer local function.
Otherwise, in OCaml you could also enjoy open (aka polymorphic) variants :
type t = [`A | `B | `C]
let f = function
| (`A | `B as x) ->
let s = match x with `A -> "a" | `B -> "b" in
print_endline s
| `C -> print_endline "ugh"
I would just put the common logic in a local function, should be both faster and more readable. Matches nested that way is pretty hard to follow, and putting the common logic in a local function allows you to ditch the extra matching in favour of something that'll get inlined anyway.
Hmm looks like you need to design the data type a bit differently such as:
type AorB =
| A of int
| B of int * int
type AllPossible =
| AB of AorB
| C of int
.... other values
let foo x =
match x with
| AB(v) ->
match v with
| A(i) -> () //Do whatever need for A
| B(i,v) -> () // Do whatever need for B
| _ -> ()
Perhaps the better solution is that rather than
type All =
|A of int
|B of int*int
you have
type All =
|AorB of int * (int Option)
If you bind the data in different ways later on you might be better off using an active pattern rather than a type, but the result would be basically the same
I don't really agree that this should be seen as a bug - although it would definitely be convenient if the case was handled by the compiler.
The C# compiler doesn't complain to the following and you wouldn't expect it to:
var b = true;
if (b)
if (!b)
Console.WriteLine("Can never be reached");