在Clojure中我会写上面的代码:
user=> (def points [{:x 11 :y 12} {:x 21 :y 22}])
#'user/points
user=> (map :x r)
(11 21)
我可以这样做,因为:x可以用作函数。这是一个非常有用的功能
F#中的相同代码如下所示:
> type Point = {x : int; y: int};;
> let points = [{x=11;y=12}; {x=21; y=22}];;
> List.map (fun p -> p.x) points
val it : int list = [11; 21]
因为我讨厌写匿名函数,所以我发现自己写的是Point类型:
type Point =
{
x: int
y : int
}
static member getX p = p.x;;
......这让我有可能做到:
> List.map Point.getX points
这仍然很混乱,因为我需要为我使用的每个记录成员编写一个getter。
相反,我想要的是这样的语法:
> List.map Point.x points
有没有办法在不必编写凌乱的匿名函数(fun p - > p.x)或静态getter的情况下执行此操作?
更新:
顺便说一句,Haskell也和Clojure一样(实际上是另一种方式):
Prelude> data Point = Point { x :: Int, y :: Int}
Prelude> let p = Point { x = 11, y=22}
Prelude> x p
11
更新2:
希望在没有帮助的情况下类型推断不起作用时,反对lambda的一个更明显的原因就是一个例子:
type Point2D = { x : int; y : int}
type Point3D = { x : int; y : int; z : int}
let get2dXes = List.map (fun (p:Point2D) -> p.x)
let get2dXes' : Point2D list -> int list = List.map (fun p -> p.x)
let get2dXes'' (ps : Point2D list) = List.map (fun p -> p.x) ps
......这样的方式不像以下那样优雅:
let get2dXes = List.map Point2D.x
我不想开始关于哪种语法更好的火焰战争。我真诚地希望有一些优雅的方式来完成上述工作,因为我自己找不到任何东西。
显然,我能做的就是向F#的强大神灵祈祷,在未来的版本中包含类似这样的特征,在类型类旁边;)
更新3:
此功能已针对未来的语言版本提出。 https://fslang.uservoice.com/forums/245727-f-language/suggestions/5663326-syntax-for-turning-properties-into-functions谢谢JackP。!
答案 0 :(得分:4)
我的推荐是这样的:
namespace Temp
type Point = { x:int; y:int }
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Point =
let x: Point -> int = fun p -> p.x
let y: Point -> int = fun p -> p.y
type Circle = { r: int; x: int; y: int }
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Circle =
let x: Circle -> int = fun c -> c.x
let y: Circle -> int = fun c -> c.y
let r: Circle -> int = fun c -> c.r
module test =
let p1 : Point = { x = 1; y = 2}
Point.y p1 |> printf "%i"
如果您可以保证不重复使用记录字段名称,那么您可以使用AutoOpen
这样的属性:
namespace Temp
type Point = { x:int; y:int }
[<AutoOpen>]
module PointExt =
let x: Point -> int = fun p -> p.x
let y: Point -> int = fun p -> p.y
module test =
let points = [ { x = 1; y = 2}; { x=5;y=10} ]
points |> List.map x |> List.iter (printf "%i")
但这种编码风格对我来说并不那么明显。我更喜欢保留模块名称。
答案 1 :(得分:1)
您通常会使用匿名函数来执行此操作,这被认为是惯用的。为记录字段定义静态“getter”似乎是多余的,并且会添加许多样板。
“乱七八糟”的匿名函数只是您的个人意见,您应该克服它。虽然我不得不承认我认为Clojure的语法是使用你自己的术语“混乱”,所以我可以看到你来自哪里;)
答案 2 :(得分:1)
这看起来怎么样?
type Point = { x: int, y : int }
let l = [{x=1;y=2};{x=3;y=4};{x=5;y=6}]
l |> List.map(function | x->x.x) // ie. based on a pattern match (see below)
虽然不多。
l |> List.map(fun x->x.x)
List.map(fun x->x.x) l
只是尝试匹配 List.map Point.x points
的目标我倾向于考虑&#34;功能&#34;作为pattern matching的捷径。
Rich Hickey使用短语&#34;现在它已经归结为熟悉&#34;在他的一次会谈中。一旦你习惯了这些例子,这些例子看起来很优雅。
功能语言都是关于&#34;函数是值&#34;,或者函数应该像处理值一样容易处理和传递。 Anon起作用摇滚,但是如果你接受那个&#34;功能|&#34;是匹配表达式的代理,那么这可能是您问题的有效答案。
更新
目前没有办法以Clojure的方式做到这一点。这不是一个糟糕的建议。对我来说,更好的胜利就是制作&#34; List。&#34;部分可选。我们可以推断出我们正在处理一个列表。
// helpers
let map = Map.map
let map0 = List.map
let map1 = Seq.map
let map2 = Array.map
多态性在这里有所帮助,但是没有 - 类型推断的价格?
btw:在&#34;属性功能&#34;上,你可以分叉F#编译器并在你有时间/如此倾向时发出拉取请求。现在F#是开源的,这取决于我们。如果我们在回复中遗漏了某些内容,请务必将您的想法置于用户的声音中。