【问题标题】:Type inference on ToString() vs string operatorToString() 与字符串运算符的类型推断
【发布时间】:2017-05-13 06:37:57
【问题描述】:

注意:这个问题和我的previous one有点关系,但其实是从不同的角度触及到的问题

考虑以下sn-p:

let toStr a = a.ToString()
let strOp a = string a

let intToStr = 5 |> toStr
let floatToStr = 5.0 |> toStr

let intStrOp = 5 |> strOp
let floatStrOp = 5.0 |> strOp //type inference error

虽然strOp 函数使用看起来更优雅的解决方案,并且也能够将单位值转换为字符串,但它似乎并不是真正的通用,因为它的类型在第一次使用时受到了限制(即使是推断的类型是obj -> string,而不是'a -> string)

为什么字符串运算符不能以这种通用方式工作?还是我做错了什么?

【问题讨论】:

  • let inline strOp a = string a
  • 这是实现更高种类多态性的著名技巧吗?如果我的问题与此有关,为什么 ToString 示例有效?我不明白其中的区别。
  • 因为toStr 是一个真正的泛型函数,而strOp 实例化一个具有静态解析类型约束的内联函数,但它本身不是内联的,因此不会继承泛型。这可能有助于解释细节:stackoverflow.com/questions/30445828/…

标签: generics f# type-inference


【解决方案1】:

不同之处在于string 使用静态成员约束(请参阅the definition),而ToString 是可用于任何对象的普通方法,因此编译器将ToString 调用视为不约束任何方式的实例类型。

静态约束和(非静态)泛型在(否则不受约束)let-bound 函数中以不同的方式工作:

  • 对于泛型代码,编译器传播泛型并使您编写的let绑定函数也泛型。

  • 对于静态成员约束,编译器会根据第一次使用专门化代码。如 cmets 中所述,您可以通过使用 inline 来避免这种情况,它允许基于静态成员的泛型以与序号泛型代码相同的方式传播。

我认为string 函数使用静态解析类型约束的唯一原因是它允许它专门化为普通的ToString 调用原始类型,但仍以自定义方式处理null 值对象 - @ 987654333@ 抛出异常但strOp null 返回一个空字符串!

【讨论】:

  • 我不认为处理空值是string 具有静态约束的原因。如果您查看its definition,这些类型特化看起来相当面向性能 - 即为已知的原始类型生成call 而不是callvirt。并且空值检查本身在anyToString 中通过装箱完美地执行,没有静态约束。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
  • 2019-03-26
  • 2016-11-30
相关资源
最近更新 更多