Case access from F # DU

Suppose I have the following DU:

type Something = | A of int | B of string * int 

Now I use it in a function like this:

 let UseSomething = function | A(i) -> DoSomethingWithA i | B(s, i) -> DoSomethingWithB si 

This works, but I had to deconstruct the DU to pass it to the DoSomethingWith * functions. It seems natural to me to try to define DoSomethingWithA as:

 let DoSomethingWithA (a: Something.A) = .... 

but the compiler complains that type A is not defined.

It seems that in accordance with the F # philosophy, you want to limit the argument to being Something.A, and not just an old int, so I'm just wrong?

+4
source share
3 answers

It is important to note that A and B are constructors of the same type Something . Thus, you will receive a warning about an inexhaustible pattern if you try to use cases A and B separately.

IMO, deconstructing all DU cases is a good idea because it is type safe and makes you think about handling these cases even if you don't want to. A problem may arise if you have to deconstruct the DU again the same way. In this case, defining map and fold functions on DU might be a good idea:

 let mapSomething fa fb = function | A(i) -> fa i | B(s, i) -> fb si 

Please refer to @Brian 's excellent catamarphism series to learn about the DU fold.

It also says your example is fine. What you are actually processing are the string and int values โ€‹โ€‹after deconstruction.

You can use Active Templates to share two cases:

 let (|ACase|) = function A i -> i | B _ -> failwith "Unexpected pattern B _" let (|BCase|) = function B(s, i) -> (s, i) | A _ -> failwith "Unexpected pattern A _" let doSomethingWithA (ACase i) = .... 

but the output type doSomethingWithA remains the same, and you get an exception when passing B _ function. So itโ€™s wrong to do IMO.

+5
source

A not a type, it is just a constructor for Something . There is no way to avoid matching patterns, which is not necessarily a bad thing.

However, F # offers a thing called active patterns, for example

 let (|AA|) = function | A i -> i | B _ -> invalidArg "B" "B not allowed!" 

which you can use as follows:

 let DoSomethingWithA (AA i) = i + 1 

But there is no real reason why you want to do this! You still run the same old template matching under the hood, plus you run the risk of a run-time error.

In any case, your UseSomething implementation UseSomething completely natural to F #.

+3
source

The other answers are accurate: there are constructors in F # A and B , not types, and this is the traditional approach used by strongly typed functional languages โ€‹โ€‹such as Haskell or other languages โ€‹โ€‹of the ML family. However, there are other approaches - I believe that in Scala, for example, A and B will actually be subclasses of Something , so you can use those more specific types where it makes sense to do this. Iโ€™m not quite sure what trade-offs are involved in the design decision, but generally speaking, inheritance makes it difficult to deduce / impossible (and itโ€™s true for deriving a stereotype type in Scala much worse than in Haskell or in the ML languages).

+3
source

Source: https://habr.com/ru/post/1469437/


All Articles