F#函数返回多种类型中的任何一种

时间:2017-01-17 05:51:16

标签: f# f#-3.0

我的场景是用户输入要执行的文本的DSL。 text可以是数字操作,也可以是字符串或数字的关系操作(返回布尔值)。 在用户输入中,可以有常量值或变量(其中变量值必须从另一个源/字典获取并在操作中使用)

示例(数字操作)

fileActions.select()

关系操作示例

(1 + 2)
(@variableName@ + 2)   //returns int

我有DU代表数字

(@countryname@ in {abc,def,ghi}) //returns bool over variable with string value
(3 > 5) //returns bool over int
(@VariableInt@ > 5) // bool over variable having int value

用于解释我的表达式/变量我有类似的东西,从这里得到variableName我想要返回字符串或int 所以我的RelationExpression | In可以得到字符串值

type NumberExpression
| VariableName of string  //variable name
|Constant of int
| Add of NumberExpression * expression

type RelationExpression
 | Boolean of bool
 | GT of NumberExpression * NumberExpression 
 | In of NumberExpression * string list

对于变量的数字运算,对于relationexpression,因为它返回变量类型是int。现在我必须处理返回字符串值的变量的返回类型。  我的NumberExpression |变量如何返回int / string

我可以尝试创建一个float和string的鉴别器联合,我的函数将返回这个新的Discriminator联合,但现在问题是默认情况下浮点运算在我的新鉴别器联合上是无效的(即使它是浮点数值)。

let rec Interpret input =
 match input with
 | VariableName(name) -> 
     let stringvalue,datatype = datadictionary.[name] //assume value is from map. I need to do typecase and either return string or int here???
 | Constant(value) ->value

虽然我的函数返回内部Float,但它的新联合类型并没有+运算符。因此使用DU返回任何一种类型的方法对我来说都不适用。如何处理?

1 个答案:

答案 0 :(得分:4)

这是你可以做到的一种方式。我将首先显示代码,然后解释它:

type myResultType = 
   | Number of float
   | StringItem of string

// I fleshed out your `myFunction` example so it actually compiles
let myFunction (input:string) = 
  match input.[input.Length-1] with
  | 'F' -> input.[0..input.Length-2] |> float |> Number
  | 'S' -> input.[0..input.Length-2] |> StringItem

// Use this one for unary operators (no examples given here)
let floatOp op result =
    match result with
    | Number n -> op n |> Number
    | anythingElse -> anythingElse

// Use this one to define operators where the "real" float is the first operand
let floatOp2 op floatArg result =
    match result with
    | Number n -> op floatArg n |> Number
    | anythingElse -> anythingElse

// Use this one to define operators where the "real" float is the second operand
let floatOp2' op result floatArg =
    match result with
    | Number n -> op n floatArg |> Number
    | anythingElse -> anythingElse

let (+.) = floatOp2 (+)
let ( *. ) = floatOp2 (*)   // Spaces needed so this doesn't look like a comment
// etc.
let (.+) = floatOp2' (+)
// etc.

let finalValue = (10.0) +. (myFunction "200F")
let finalValue' = (myFunction "200F") .+ 10.0

您无法重新定义现有的+运算符,但可以创建自己的运算符,该运算符将在Number个案例中运行(如果您的结果不执行任何操作type是StringItem个案例。由于所有这些运算符的公共代码看起来几乎相同,因此我将其提取到自己的函数中。然后,您可以定义-./.等运算符,其方式与您定义+.*.运算符的方式非常相似。

注意在定义*.运算符时如何在括号内放置空格。只要您定义以*字符开头(或结束)的自定义运算符,就必须这样做。否则,(**)看起来就像是评论的开头。例如,let (*!*) arg1 arg2 = ...将F#解析器视为包含单个感叹号的注释,并且它认为您正在定义名为arg1的函数,该函数采用名为arg2的单个参数。但是,let ( *!* ) arg1 arg2 = ...将被正确解析为定义带有两个参数的运算符*!*