F# How to have a value's type determined by a match statement?

落花浮王杯 提交于 2019-12-13 04:46:10

问题


Here is my problem:

let foo =
    match bar with
            | barConfig1 ->                configType1(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
            | barConfig2 -> configType2(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
            | barConfig3 -> configType3(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
            | barConfig4 -> configType4(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)

I'd like to have the type of foo be determined by the match statement, but it always sets foo to the first type.

type bar =
|barConfig1
|barConfig2
|barConfig3
|barConfig4

回答1:


In F#, there are no statements, only expressions, and each expression has to have a single concrete type. A match block is an expression as well, meaning that it has to have a single concrete type. What follows from that is that each case of the match has to have the same type as well.

That is, something like this is not valid F#:

let foo =               // int? string?
    match bar with      // int? string?
    | Int    -> 3       // int
    | String -> "Three" // string

In this case, the type inference mechanism will expect the type of the match to be the same as the type of the first case - int, and end up confused when it sees the string in the second. In your example the same thing happens - type inference expects all the cases to return a configType1.

A way around it would be by casting the values into a common supertype or interface type. So for your case, assuming the configTypes implement a common IConfigType interface:

 let foo =   // IConfigType
    let arg = (devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
    match bar with
    | barConfig1 -> configType1(arg) :> IConfigType
    | barConfig2 -> configType2(arg) :> IConfigType
    | barConfig3 -> configType3(arg) :> IConfigType
    | barConfig4 -> configType4(arg) :> IConfigType



回答2:


If the output type has a limited number of cases, you can make that a discriminated union as well:

type ConfigType =
    | ConfigType1 of configType1
    | ConfigType2 of configType2
    | ConfigType3 of configType3
    | ConfigType4 of configType4``

let foo =
    match bar with
        | barConfig1 -> ConfigType1 <| configType1(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
        | barConfig2 -> ConfigType2 <| configType2(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
        | barConfig3 -> ConfigType3 <| configType3(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
        | barConfig4 -> ConfigType4 <| configType4(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)``

Alternately, if they all implement an interface or inherit some base class, you can upcast to that, as with scrwtp's answer.



来源:https://stackoverflow.com/questions/26937533/f-how-to-have-a-values-type-determined-by-a-match-statement

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