8

In Clojure I would write the above code:

user=> (def points [{:x 11 :y 12} {:x 21 :y 22}])
#'user/points
user=> (map :x r)
(11 21)

I can do this because :x can be used as a function. This is a very useful feature

The same code in F# would look like this:

> 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]

Because I hate writing the anonymous function all the time I find myself writing the type Point like this:

type Point =                 
    {                        
        x: int               
        y : int              
    }                        

    static member getX p = p.x;;

...which gives me the possibility of doing:

> List.map Point.getX points

This is still messy because I need to write a getter for each record member that I use.

Instead what I would like is a syntax like this:

> List.map Point.x points

Is there a way to do this without having to write the messy anonymous function (fun p -> p.x) or the static getter?

UPDATE:

By the way, Haskell also does it the same as Clojure (actually is the other way around):

Prelude> data Point = Point { x :: Int, y :: Int}
Prelude> let p = Point { x = 11, y=22}
Prelude> x p
11

UPDATE 2:

Hopefully a more obvious reason against the lambda is an example when the type inference doesn't work without help:

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

...which are way less elegant than something like:

let get2dXes = List.map Point2D.x

I don't want to start flame wars about which syntax is better. I was just sincerely hoping that there is some elegant way to do the above since I haven't found any myself.

Apparently all I can do is pray to the mighty gods of F# to include a feature like this in a future version, next to the type classes ;)

UPDATE 3:

This feature is already proposed for a future language version. https://fslang.uservoice.com/forums/245727-f-language/suggestions/5663326-syntax-for-turning-properties-into-functions Thanks JackP.!

vidi
  • 2,056
  • 16
  • 34
  • The compile would have to generate these in an [`AutoOpen`](http://stackoverflow.com/questions/12977466/autoopen-attribute-in-f) marked module. There would also have to be the equivalent of overload resolution on the type passed into the method (for when several records use the same field names). – Christopher Stevenson Oct 25 '14 at 18:15
  • I think you don't actually need to care about overloads, just have one module per type, then which function to use would be inferred from record type. – scrwtp Oct 25 '14 at 18:25
  • 1
    There is a suggestion to add the Clojure-like syntax you want as a language feature in a future version of F#. Please make sure to vote on the feature if you want it: https://fslang.uservoice.com/forums/245727-f-language/suggestions/5663326-syntax-for-turning-properties-into-functions – Jack P. Oct 25 '14 at 21:26
  • @JackP. Nice. I can see it's one of the top suggestions. Hopefully will be addressed. Thanks for the link! – vidi Oct 25 '14 at 21:39
  • It appears that this suggestion is related to the following GitHub issues: * https://github.com/fsharp/fslang-suggestions/issues/506 * https://github.com/fsharp/fslang-suggestions/issues/159 – jpierson Apr 14 '19 at 01:47

3 Answers3

4

My recommendation would be something like this:

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"

If you could guarantee no reuse of record field names, then you could use the AutoOpen attribute like this:

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")

This style of coding feels less obvious to me, though. I'd prefer to keep module names.

Christopher Stevenson
  • 2,843
  • 20
  • 25
  • Still tedious but looks better than having the Point.getX static member. I see this as a workaround until the property-function feature will be implemented – vidi Oct 25 '14 at 23:07
  • This is not really a fix for my problem because I have to write those small property-functions but it's a step forward so I accept this answer – vidi Oct 25 '14 at 23:22
  • could you expand on your use of ModuleSuffix? How does this help? – sgtz Oct 26 '14 at 06:19
  • It lets you use a type and module of the same name, combining the two of them together into one .Net object on MSIL form. It's my preferred method to identify a module that's an extension of a type. – Christopher Stevenson Oct 26 '14 at 06:59
  • I agree with @ChristopherStevenson here. The only thing that I don't like is the "[]" part which is verbose and messy. Instead, if I do this often I would like a helper that would look like this: type ModuleSuffixAttribute() = inherit CompilationRepresentationAttribute(CompilationRepresentationFlags.ModuleSuffix) Unfortunately it doesn't work because CompilationRepresentationAttribute is sealed. – vidi Oct 26 '14 at 09:43
  • Still, I see this as a workaround until F# supports Point.x as a function (hopefully). Then I can remove the module suffix and the code remains unchanged – vidi Oct 26 '14 at 09:47
2

how does this look?

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)

although not much in it.

l |> List.map(fun x->x.x)

List.map(fun x->x.x) l

Just trying to match your goal of List.map Point.x points

I tend to think of "function" as a short cut to pattern matching.

Rich Hickey uses the phrase, "now it is down to familiarity" in one of his talks. The examples look kind of elegant once you are used to them.

Functional languages are all about a "function is value", or functions should be as easy to handle and pass around as a value. Anon functions rock, but if you'll accept that "function |" is a proxy to a match expression, then maybe this is a valid answer to your question.


Update

no there is not a way to do this in a Clojure fashion currently. It's not a bad suggestion. For me the nicer win would be to make the "List." portion optional. We can infer that we are dealing with a list.

// helpers
let map = Map.map
let map0 = List.map
let map1 = Seq.map
let map2 = Array.map

Polymorphism would help out here, but there isn't any -- the price of type inference?

btw: on the "property feature", you could fork the F# compiler and make a pull request if you have the time / are so inclined. It's up to us now that F# is open source. Definitely put your thoughts into user voice if there's something that we've missed in our replies.

sgtz
  • 8,849
  • 9
  • 51
  • 91
  • There is not much of a difference between (fun p -> p.x) and (function | p -> p.x). Regarding the "Now it is down to familiarity", if you suggest this is not a useful feature but instead it's just a matter of taste I disagree with you. The lambda is verbose and you may find situations when the type inference doesn't work and it becomes even more verbose because you have to help the type system something like (fun (p : Point) -> p.x) which becomes even uglier. I'll give an example in the question text. – vidi Oct 25 '14 at 21:15
  • @vidi: I have made a 'more elegant fun x-> please' request in person (similar to Gustavo's user voice request). However, I don't find this syntax annoying anymore. I guess it is familiar to me now. Being a touch more explicit helps out sometimes. On balance for me it's okay as it helps me see the overall flow when combined with |>, etc. – sgtz Oct 26 '14 at 05:34
  • @vidi: did a small Update. – sgtz Oct 26 '14 at 05:55
1

You would normally use an anonymous function to do it and it is considered idiomatic. Defining a static "getter" for a record field seems superfluous and would add lots of boilerplate.

Anonymous functions being "messy" is only your personal opinion and you should get over it. Though I have to admit that I consider Clojure's syntax for them to be, using your own term, "messy", so I can see where you're coming from ;)

scrwtp
  • 13,437
  • 2
  • 26
  • 30
  • I agree that the static getter is not ok but having all those small lambdas all over the code looks bad. I was hoping that there is a better way. – vidi Oct 25 '14 at 17:06
  • Well, you could bind reused lambda's in a suffix module. – Christopher Stevenson Oct 25 '14 at 18:08
  • @ChristopherStevenson: That's clever, in a crazy 'don't go there' way. Why not make it an answer? – scrwtp Oct 25 '14 at 18:22
  • @vidi: Well, this is how the language looks. When you write F#, you use small lambdas. You can make an effort not to do that and do some crazy stuff instead, but then you're fighting against the language instead of using it. – scrwtp Oct 25 '14 at 18:27
  • I would say this is how the language looks NOW. Having seen Clojure and Haskell take on records it's hard for me not to appreciate its advantage and I think F# should adopt such a syntax – vidi Oct 26 '14 at 09:50