Reputation:
I am trying to figure out how to handle patternmatching cases where it returns different types depending on the match, I know it has to return the same type for each branch, so im not sure what the "proper" way to handle a situation like this is:
I tried to make an example of my confusion below, paintArr being an array representing a pallete that can contain Some color or an empty slot.
paintArr.[i,j].color being of type Color option, containing Black and white.
The pattern matching should for each slot decide wether the color is black or white and add its index to the appropriate array.
let sort (paintArr: Pallete) =
let black = [||]
let white = [||]
for i = 0 to 5 do
for j = 0 to 5 do
match ((Option.get (paintArr.[i,j])).color) with
| White -> Array.append white paintArr.[i,j]
| Black -> Array.append black paintArr.[i,j]
| None -> "not sure what to do here"
(black, white)
So essentially, I think my question boils down to: how do you handle situations like this, where I in some cases will get a match that requires me to do nothing, or potentially just something different from the other cases?
Upvotes: 3
Views: 809
Reputation: 4895
For different return types I would suggest using discriminated unions. You can read more also here. But by the code you have provided, it does not seem to be matching the question itself. I would suggest first of all refactoring your initial function to be cleaner in coding style.
Upvotes: 2
Reputation: 80734
Preamble: it is obvious to me that you're very inexperienced with F#. If this is true, I would recommend you to first read some sort of book or a set of tutorials (I always recomment https://fsharpforfunandprofit.com/). You're trying to work with code that is just a bit more complicated than a complete beginner should handle.
First of all, note that Array.append
doesn't "change" ("update", "modify") the array, but instead returns a new array - a concatenation of the original array you gave it and the new element. With this knowledge, it's easy to see that your Array.append
calls are useless: they return something, but you just instantly throw it away.
From the context, I understand that what you actually wanted to do is to replace the array in question with the extended version of it. To do that, you need to declare your arrays mutable
, and then use the "destructive update" operator <-
:
black <- Array.append black paintArr.[i,j]
The destructive update operator <-
in F# is an equivalent of the assignment statement =
in C-like languages.
Now, if you use the destructive update operator like this, the resulting type of such expression will be unit
- an empty type, used to denote "no value". This type has only one value, and it is written as two parentheses with nothing between them. For example:
> let x = ()
val x : unit = ()
So to achieve the "same type from all branches" rule, you can have your third branch simply return a unit
value, without calling any functions:
| None -> ()
Applying all of the above to your code, we get this:
let sort (paintArr: Pallete) =
let mutable black = [||]
let mutable white = [||]
for i = 0 to 5 do
match ((Option.get (paintArr.[i,j])).color) with
| White -> white <- Array.append white paintArr.[i,j]
| Black -> black <- Array.append black paintArr.[i,j]
| None -> ()
(black, white)
However, note that this still won't compile, because there are other errors in it. Firstly, Option.get
will only work when paintArr.[i,j].color
is a Some
value, but will crash when it's None
. Secondly, even when this function does succeed, it will return you a color, but then you're trying to compare it with None
, which is not a color. This will produce a compile-time error.
It's a bit hard to infer what you actually wanted to do at this point, but I will try to guess. I will guess that paintArr.[i,j].color
is of type Color option
, where Color
is an enumeration that includes White
, Black
, and some other colors.
If that is true, you need to match not on Option.get( ... )
, but on paintArr.[i,j].color
itself, and handle three cases: (1) when the color is a Black
wrapped in Some
, (2) when the color is a White
wrapped in Some
, and (3) when the color is None
:
for i = 0 to 5 do
match paintArr.[i,j].color with
| Some White -> white <- Array.append white paintArr.[i,j]
| Some Black -> black <- Array.append black paintArr.[i,j]
| None -> ()
Finally, I can see a loop for i
, but where is j
? I guess you simply forgot to add a loop for it:
for i = 0 to 5 do
for j = 0 to 5 do
match paintArr.[i,j].color with
| Some White -> white <- Array.append white paintArr.[i,j]
| Some Black -> black <- Array.append black paintArr.[i,j]
| None -> ()
Upvotes: 7