F# pattern match directly against let binding

倾然丶 夕夏残阳落幕 提交于 2020-01-11 10:04:34

问题


Is it possible in F# to pattern match directly against a let binding?

For example, this compiles without any warnings:

    let value = 
        match arg with
        | 1 -> "value1"
        | 2 -> "value2"
        | _ -> failwith "key not found"

Whereas the following gives the warning "This rule will never be matched" against the lines matching key2 and _:

    let key1 = 1
    let key2 = 2
    let value = 
        match arg with
        | key1 -> "value1"
        | key2 -> "value2"
        | _ -> failwith "key not found"

Is this because although they're immutable, the let bindings are unlike C# const variables?


回答1:


just use capital letters and [<Literal>] them and it works as expected.

let [<Literal>] X = 0
let [<Literal>] Y = 1
let bla arg =
    match arg with
    | X -> "zero"
    | Y -> "one"
    | somethingelse -> somethingelse.ToString()

the lower case name by convention typically means a wildcard that is bound to the name.




回答2:


The reason you're getting that error is because of what F# is doing when you use a variable name in the pattern clause of a match expression.

Let's say I have

match arg with
| x when x = 0 -> "zero"
| y when y = 1 -> "one"
| _ -> "other"

I think it's key here to note that, despite not defining x or y prior to the match, this code will still work. This is because x and y are just short codes which makes writing match expressions easier. Behind the scenes, the F# compiler is actually converting that x when x = 0 into "let binding" where x is bound to arg. x can then be used in the x = 0 expression and in the expression after the ->.

Going back to the problem you ran into:

let key1 = 1
let key2 = 2
let value = 
    match arg with
    | key1 -> "value1"
    | key2 -> "value2"
    | _ -> failwith "key not found"

The reason this won't work, is because in the match expression, F# is rebinding key1 to the value of arg, so key1 -> "value1" is equivalent toif arg1 = arg1 then "value1". The first pattern will always be matched; so, key2 and _ will never be reached.

I'm not sure how clear my explanation is, so I'll also throw in a second approach to explaining what's happened:

if you translate the match expression into an if-else it would look like this:

let key1 = 1
let key2 = 2

let value = 
    if let key1 = arg in arg = key1 then
        "value1"
    else if let key2 = arg in arg = key2 then
        "value2"
    else
        failwith "key not found"

(why yes, F# will let you throw let bindings into if expressions)

This if/else expression is equivalent to your match expression. In this form it becomes clear that the first condition will always evaluate to true.

I won't put it in here, but it may help to look at the code quotation of a match expression. I didn't really get what was going on with match expressions until I saw what the abstract syntax tree they generated looked like.




回答3:


You can only use literals if you want to match against a particular value in a pattern matching case. An identifier means binding -- i.e. the actual value in this pattern matching case will be bound to the identifier that will be visible in the scope of this case.

As @DanielFabian has shown, you can define your own literals and give them names.

If the value you need to match against isn't known at compile time, you can use guards like so:

match arg with
| x when x = key1 -> "value1"
| x when x = key2 -> "value2"
| _ -> // etc

See the MSDN article on pattern matching for more details.




回答4:


There are two main issues raised by the code you are trying to use.

Firstly pattern matching:

match arg with
| _ -> "value"

matches arg with anything and then returns "value"

match arg with
| a -> "value"

matches arg with anything, calls it "a", and then returns "value". You can think of the match having it's own little namespace, the a only exists in the match, and the match only 'sees' the things that have been named in it.

Secondly, if you want to match to a set of predefined values, then you probably want to use discriminated unions. You can define one like this:

type Keys=
| Key1
| Key2

Then match like this:

match arg with
| Key1 -> "value1"
| Key2 -> "value2"

In this case it matches against the type Keys, not a value called Key1 or Key2.



来源:https://stackoverflow.com/questions/22424812/f-pattern-match-directly-against-let-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!