在我看来,F#选项类型的某些属性在C#项目中是不可见的.通过检查类型,我可以看到或多或少的原因,但我真的不明白究竟发生了什么,为什么做出这些选择或如何最好地规避问题.
这里有一些片段展示了这个问题.我有一个包含两个项目的VS2015解决方案,一个C#项目和一个F#项目.在F#项目中,我有一个类定义如下:
type Foo () =
member this.Bar () = Some(1)
Run Code Online (Sandbox Code Playgroud)
此外,在F#中,我可以这样写:
let option = (new Foo()).Bar()
let result = if option.IsNone then "Is none" else "Is some"
Run Code Online (Sandbox Code Playgroud)
所以看起来选项类型有一个名为的属性IsNone.现在,在C#项目中,我引用了从F#项目编译的.dll.这允许我写例如
var optionType = new Foo().Bar();
Run Code Online (Sandbox Code Playgroud)
变量optionType是一个FSharpOption<int>.如上所述,当我在F#项目中使用选项类型时,我通常可以访问例如IsSome和IsNone属性.但是,当我尝试写类似的东西时optionType.IsNone,我得到CS1546错误"属性,索引器或事件......语言不支持".与此一致,Intellisense没有检测到属性:
现在,在检查FSharpOption类型时,我可以看到IsNone和IsSome"属性"显示为静态方法:
另一方面,当我从F#检查类型时,我看到以下内容:
在这里,属性的"存在" IsSome,并IsNone可见一斑.将光标悬停在这些属性上,VS2015给出了以下注释:"包含类型可以使用'null'作为其nullary union case的表示值.该成员将被编译为静态成员." 这就是除了静态方法(如lukegv和Fyodor Soikin所述)之外,这些属性不可用的原因.
因此,情况似乎如下:编译的FSharpOption类型没有任何IsNone和IsSome属性.F#中的幕后工作正在进行,以启用模拟这些属性的功能.
我知道我可以通过使用OptionModuleMicrosoft.FSharp.Core 来解决这个问题.但是,似乎这个功能是F#核心库的架构师有意识的选择.选择的原因是什么?并且正在使用OptionModule正确的解决方案,还是有更好的方法来使用FSharpOption<T>C#中的类型?
我在F#中有一个带有单个类型参数的泛型类,并且想要创建一个包含工厂方法的静态类.当我编写类时,F#编译器会生成与"转换其范围的类型变量"相关的错误.我的问题是为什么错误存在以及如何解决它.
我创建了一个最小尺寸代码段来演示此问题:
type Foo<'a>(element : 'a) =
member this.Copy () = Bar.Create(element)
and Bar =
static member Create(element : 'a) = new Foo<'a>(element)
Run Code Online (Sandbox Code Playgroud)
类型中的相互递归是因为我希望类型Foo<'a>能够在静态类中调用工厂方法.上面的代码片段没有编译,错误是:"类型推断导致类型变量a逃避其范围.考虑添加显式类型参数声明或调整代码以减少通用." 错误被注册为位于类的Create方法中Bar.不幸的是,我并不是真的了解这个问题,也不知道如何解决它.有任何想法吗?
这是另外一个观察.片段
type Foo<'a>(element : 'a) =
member this.Element = element
and Bar =
static member Create(element : 'a) = new Foo<'a>(element)
Run Code Online (Sandbox Code Playgroud)
编译.所以问题似乎与基于类Copy()方法的类型推断有关Foo<'a>.此外,该片段
type Foo<'a>(element : 'a) =
member this.Copy () = Bar.Create(element)
and Bar =
static member Create<'a>(element) = new Foo<'a>(element)
Run Code Online (Sandbox Code Playgroud)
是一个更像C#的代码版本(其中静态方法明确地是通用的),它也没有编译,错误"这段代码不够通用.类型变量'a不能一概而论,因为它会逃避其范围."
F#支持"切片表达式",例如对于传统的一维数组,其myArray允许诸如的表达式myArray.[3 .. 5].根据例如F#4.0语言规范(第6.4.7节),这是通过在适当转换参数后调用GetSlice方法来实现的.这也适用于多维数组.但是,我在定义在二维情况下实现它的接口时遇到了一些麻烦.
我所做的是以下内容.我已经定义了一个接口如下:
type IMatrix =
abstract member GetSlice : ?start1:int * ?end1:int * ?start2:int * ?end2:int -> IMatrix
abstract member GetSlice : idx1:int * ?end1:int * ?start2:int * ?end2:int -> IMatrix
abstract member GetSlice : ?start1:int * ?end1:int * idx2:int -> IMatrix
Run Code Online (Sandbox Code Playgroud)
这基于我从说明书第6.4.7节中理解的说明.我的想法是,当我有一个IMatrix名字时matrix,我应该能够写作
matrix.[1 .. 2, 3 .. 4]
Run Code Online (Sandbox Code Playgroud)
并得到一个类型的子矩阵IMatrix.这个想法本质上是由编译器1 .. 2转换Some 1, Some 2并3 .. 4转换为Some 3, Some 4,并且这四种选项类型都给出了四参数 …
arrays f# functional-programming interface multidimensional-array
我在F#中编写了一个很小的矩阵库(主要是包装器方法),并且有一个关于静态运算符方法重载的问题,F#选择了一个我不打算过载的重载.
我有一个模块,我已经定义了矩阵与向量的右乘法:
[<AutoOpen>]
module MatrixOps =
let (*) (matrix : IMatrix) (vector : IVector) =
(...)
Run Code Online (Sandbox Code Playgroud)
这让我可以编写像A*v这样的东西,其中A是IMatrix而v是IVector.但是,我现在在上面的let-binding下面添加以下行:
let z = 1.0 * 2.0
Run Code Online (Sandbox Code Playgroud)
然后,F#编译器将此识别为错误.将鼠标悬停在"1.0"上,我得到:"类型'浮点'与'IMatrix'类型不兼容,同样,悬停在"2.0"上,我得到:"类型'浮点'与类型不兼容' IVector'".这里发生的事情似乎是F#编译器无法对浮点数应用乘法运算符,而是将运算符应用于IMatrix和IVector.如果我反而写
let z = (1.0 : float) * (2.0 : float)
Run Code Online (Sandbox Code Playgroud)
问题仍然存在,因此添加显式类型注释并没有帮助.如何确保F#选择浮动乘法运算符而不是上面定义的IMatrix/IVector运算符?
我正在尝试为IEquatable<T>F#中的特定类实现.但是,在这个特定的情况下这样做时,我会收到意外错误.
我有以下代码:
type Foo private (name : string) =
member this.Name = name
member this.Equals (other : Foo) = this.Name = other.Name
override this.Equals other =
match other with | :? Foo as foo -> this.Equals foo | _ -> false
override this.GetHashCode () = this.Name.GetHashCode ()
interface IEquatable<Foo> with
this.Equals other = this.Equals other
Run Code Online (Sandbox Code Playgroud)
这不编译.我收到以下错误:"带有'成员定义'的意外关键字'.另外,我收到了"可能不正确的缩进..."警告.我不知道问题是什么,因为在我看来,上面接口通常是如何在F#中实现的.为什么上面没有编译?
我正在为F#中的矩阵类实现切片表达式,这意味着我必须实现三种GetSlice方法.我的问题是通过利用核心库中的现有功能来获得有效的方法.这三种方法的签名如下:
member GetSlice : idx1:int * start2:int option * end2:int option -> IMatrix
member GetSlice : start1:int option * end1:int option * idx2:int -> IMatrix
member GetSlice : start1:int option * end1:int option *
start2:int option * end2:int option -> IMatrix
Run Code Online (Sandbox Code Playgroud)
考虑这三个中的最后一个.矩阵类有一个私有float[,]字段entries,用于保存矩阵数据.最自然的实现是这样的:
member this.GetSlice (?start1 : int, ?end1 : int, ?start2 : int, ?end2 : int) =
let newEntries = entries.GetSlice(?start1 = start1, ?end1 = end1,
?start2 = start2, ?end2 = end2)
new Matrix(newEntries), …Run Code Online (Sandbox Code Playgroud) arrays f# functional-programming pattern-matching multidimensional-array