F # Pattern comparison by type

How does pattern matching by argument type work in F #?

For example, I am trying to write a simple program that would calculate the square root if a number is provided or returns it otherwise.

open System let my_sqrt x = match x with | :? float as f -> sqrt f | _ -> x printfn "Enter x" let x = Console.ReadLine() printfn "For x = %A result is %A" x (my_sqrt x) Console.ReadLine() 

I get this error:

 error FS0008: This runtime coercion or type test from type 'a to float involves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types. Further type annotations are needed. 

Since sqrt works with float , I check the type of float , but I think there might be a better solution - for example, check if the input is a number (in general), and if so, add it to the float?

+4
source share
2 answers

The problem here is that type x is actually a string . Having added that it comes from Console.ReadLine , what information is stored in this line, it can only be determined at runtime. This means that you cannot use pattern matching or pattern matching with coercion here.

But you can use Active templates . As the fact that the actual data is stored in x is known only at runtime, you need to parse the string and see what it contains.

Suppose you are expecting a float , but you cannot be sure, as the user can enter whatever he wants. We will try to parse our line:

 let my_sqrt x = let success, v = System.Single.TryParse x // the float in F# is represented by System.Single in .NET if success then sqrt v else x 

But this does not compile:

This expression should have been of type float32, but there is a line of type

The problem is that the compiler deduced a function to return float32 based on the sqrt (System.Single.Parse(x)) expression sqrt (System.Single.Parse(x)) . But then, if x does not parse the float, we intend to simply return it, and since x is a string, we have inconsistency here.

To fix this, we need to convert the sqrt result to a string:

 let my_sqrt x = let success, v = System.Single.TryParse x if success then (sqrt v).ToString() else x 

Well, this should work, but it does not use pattern matching. So let's define our โ€œactiveโ€ pattern, since we cannot use regular pattern matching here:

 let (|Float|_|) input = match System.Single.TryParse input with | true, v -> Some v | _ -> None 

Basically, this pattern will only match if input can be correctly parsed as a floating point literal. Here's how you can use it in your initial implementation of the function:

 let my_sqrt' x = match x with | Float f -> (sqrt f).ToString() | _ -> x 

This is very similar to your function, but note that I still need to add the .ToString() bit.

Hope this helps.

+5
source

Just quoting one and only Scott Ulashin "F # for pleasure and profit" :

Subtype Matching You can match subtypes using :? operator that gives you crude polymorphism:

 let x = new Object() let y = match x with | :? System.Int32 -> printfn "matched an int" | :? System.DateTime -> printfn "matched a datetime" | _ -> printfn "another type" 

This only works for finding subclasses of the parent class (in this case, Object). The general type of expression has a parent class as input.

Please note that in some cases you may need to โ€œinsertโ€ a value.

 let detectType v = match v with | :? int -> printfn "this is an int" | _ -> printfn "something else" // error FS0008: This runtime coercion or type test from type 'a to int // involves an indeterminate type based on information prior to this program point. // Runtime type tests are not allowed on some types. Further type annotations are needed. 

A message informs you of the problem: "Runtime type tests are not allowed on some types." The answer is to โ€œboxโ€ the value, which forces its reference type, and then you can enter it:

 let detectTypeBoxed v = match box v with // used "box v" | :? int -> printfn "this is an int" | _ -> printfn "something else" //test detectTypeBoxed 1 detectTypeBoxed 3.14 

In my opinion, matching and sending by type is the smell of code, just like in object-oriented programming. Sometimes it is necessary, but used carelessly, is an indicator of poor design.

In a good object-oriented design, the correct approach would be to use polymorphism to replace subtype tests, as well as methods such as double submitting. Therefore, if you do this OO in F #, you should probably use the same methods.

+1
source

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


All Articles