I suggest we ...

Syntax for turning properties into functions

When we have F# records and classes (including the ones generated by type providers), we end up having to write things like this a lot:

(fun x -> x.Name)

It would be nice to have some shorthand for this, similar to what we can do with static members (but nicer than the Scala syntax please)

457 votes
Sign in
or sign in with
  • facebook
  • google
    Password icon
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    Gustavo Guerra shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →


    Sign in
    or sign in with
    • facebook
    • google
      Password icon
      Signed in as (Sign out)
      • Abel commented  ·   ·  Flag as inappropriate

        Your title "turning properties into functions" suggests that F# wouldn't have support for this, but you can already do that (get the function accessor of the property):

        let x = classWithNameProperty()
        let f = x.get_Name // x.Name property as a function, f: unit -> string

        But without the syntax decorations proposed in the comments, I don't see how this can be turned into a function if the object is not known (which is the second part of your request).

        Unless you need this for many different properties, you can create your own object-less syntax using ducktyping, but this is "per property":

        type MyTest() =
            member __.Name = "foo"

        let inline name (x: ^a) = (^a: (member Name: string) x)

        let f() =
            Seq.singleton (MyTest())
            |>Seq.map name                // works on any object in the seq with x.Name property

        Of course, it would be much nicer to have standard syntax for this.

      • Dax Fohl commented  ·   ·  Flag as inappropriate

        Not only properties, but members of any kind. (e.g. I just found out about `Seq.map string` instead of `Seq.map (fun x -> x.ToString())` today.)

        https://fslang.uservoice.com/forums/245727-f-language/suggestions/5665355-add-syntactic-sugar-for-functions-ala-scala-clojur seems to enable this but be even more generic, and I think less ugly syntax memorization than "(%$#) turns a member into a fn".

        The only downside of that proposal I can see is that it *only* works in lambdas, but I don't see any reason I'd ever want to call a naked `%$#DoSomething myThing` instead of `myThing.DoSomething()` anyway.

      • George commented  ·   ·  Flag as inappropriate

        Actually there are already partial solutions...

        let Name a = a.Name

        This is a little more exotic and a better explanation of the principals is in the F# language reference pdf and here: http://stackoverflow.com/questions/33161244/in-f-is-it-possible-to-have-a-tryparse-function-that-infers-the-target-type

        let inline id (a:^a) = (^a : (member Id:'b) (a))

        Essentially, static type constraints can be used to not only ensure that the parameter has a desired member (or static member) but also apply that member against the parameter to obtain a result.

      • Ibrahim commented  ·   ·  Flag as inappropriate

        I was thinking about this syntax:

        let f = MyType1.Member1

        Where f is equivalent to Member1 with arguments prefix with the 'this' parameter of type 'MyType1'

      • Varon commented  ·   ·  Flag as inappropriate

        The concept of 'functional properties' has been thoroughly explored under the name of Lenses in the formal side of the FP world.

        There's an existing library that, if mixed with some code generation would probably perfectly work for this purpose. Don Syme has offered some comments relating to naming and usage in the "Conventions for Lens declarations" issue. You can read more about this on the github page at https://github.com/xyncro/aether .

        Incidentally, this isn't the first time someone's asked for functional properties in F#. Another attempt is visible in the "Implement first-class lensing / lenses in F#". This seems to be a generalization of this suggestion.

        You can view this alternative proposal here: https://fslang.uservoice.com/forums/245727-f-language/suggestions/6906132-implement-first-class-lensing-lenses-in-f .

        I would suggest that rather than implementing a less general syntactic workaround, we focus on working towards what we need for functional style properties (aka lenses).

      • Yaar Hever commented  ·   ·  Flag as inappropriate

        I like the idea of using a bare dot: List.map (.name),
        but I think a better solution would be to have a fixed keyword for creating a lambda whose first and only argument is replaced by something. I've seen the usage of a keyword "it" in some language, but I can't remember which one it was.

        This would allow not only: List.map (it.name)
        but also: List.map (funcWithTupleArguments (arg1, arg2, it))
        or even: List.map (sprintf "the square root of %f is %f\n" it (sqrt it))

      • Jared Hester commented  ·   ·  Flag as inappropriate

        This functionality does exist in the form

        .... let inline _Data x = ( ^a : ( member Data: 'b ) x )

        Which works in situations like -

        .... type Bottom = { Data:string }
        .... type Middle = { Data:Bottom }
        .... type Top = { Data:Middle }

        .... let top = { Data = { Data = { Data = "bottom" }}}
        .... let inline d3 x = ( _Data >> _Data >> _Data ) x ;;
        .... d3 top
        .... > val it : string = "bottom"

        The suggested `.` notation seems like it might cause issues with partial application unless surrounded by parens, so it's probably better to use a different operator as a unary operator on an identifier to create a function like _Data. Some possibilities are

        .... ( @. ) ___( @.Data ) <- my favorite option
        .... ( .@ ) ___( .@Data )
        .... ( @| ) ___( @|Data )
        .... ( |@ ) ___( |@Data )
        .... ( =| ) ____( =|Data )
        .... ( |= ) ____( |=Data )
        .... ( |- ) ____ ( |-Data )
        .... ( -| ) ____ ( -|Data )
        .... ( ./ ) ____ ( ./Data )
        .... ( /. ) ____ ( /.Data )
        .... ( |. ) ____ ( |.Data )
        .... ( .| ) ____ ( .|Data )
        .... ( !. ) ____ ( !.Data )
        .... ( *@ ) ___ ( *@Data )
        .... ( @* ) ___ ( @*Data )
        .... ( -@ ) ___ ( -@Data )
        .... ( @- ) ___ ( @-Data )
        .... ( |* ) ____ ( |*Data )
        .... ( *| ) ____ ( *|Data )

        ( these are all currently valid operators )

      • Greg Rosenbaum commented  ·   ·  Flag as inappropriate

        I think I prefer the #Property syntax, rather than the (.Property) syntax. This is mainly because I don't like extraneous parentheses. ~Property is also possible, but I tend to associate ~ with other things so it's a bit strange.

        It would be great if it could be extended to instance methods, and not just properties. It would make chaining methods and module functions more convenient. Something like:

        "blah" |> String.replicate 2 |> #Substring 1

      • luketopia commented  ·   ·  Flag as inappropriate

        What if we allowed the underscore to represent missing arguments to a member (including the instance), in which case a function for applying those arguments would be produced? Then we could do the following:

        |> Seq.map _.Name
        |> File.WriteAllLines(@"C:\CustomerList.txt", _)

        This would allow us to partially apply ordinary CLR methods with more than one argument, something I have always wanted.

        I do still like the .Name syntax without the underscore, but that could be a special case (allow the underscore for the instance to be dropped).

        I also think more complex expressions like _.Address.State could be supported. In this case, the entire expression would become a function. This would be distinct from (_.Address).State which would be a compiler error since (_.Address) is its own function.

      • Richard Gibson commented  ·   ·  Flag as inappropriate

        Nice to see you here, Scott!

        This would be a wonderful addition to the language, and I don't see any problem with type inference. (.name) would work wherever (fun x -> x.name) would, e.g.

        Seq.map (.name) people // => Not okay
        people |> Seq.map (.name) // => This is just fine

        I also have no problem with the tilde syntax. Perhaps then you could drop the parens? E.g.

        people |> Seq.map ~name

      • Scott Wlaschin commented  ·   ·  Flag as inappropriate

        +1 to the "(.identifier)" syntax. Alternatively, extend the preexisting custom prefix operator syntax with (~identifier). You could even use the same syntax to create infix operators the same way, a la backticks in haskell.

        For tuples you could use (.#1) (.#2) etc or if using tildes, (~1), (~2).

        As to name resolution, surely you could use the same scoping rules as for members. That is, if "x" is a valid member name in the scope of Point, then (.x) is also a valid name when applied to Points. I don't know how type inference would work though...

      • Bryan Edds commented  ·   ·  Flag as inappropriate

        To amend my previous comment, I have to say that I quite urgently would also like this functionality to work for non-record property getters now. This is because I now use type extensions to provide syntactically clean getters to dynamic record-style fields like so -

        type Entity with
        ____member entity.CharacterType = entity?CharacterType : CharacterType
        ____static member setCharacterType (value : CharacterType) (entity : Entity) = entity?CharacterType <- value

        With this, I can do this -

        let ct = character.CharacterType


        let c = Entity.setCharacterType ct c

        I know this contradicts what I said earlier, but I need this functionality very badly. Doubly so in the face of needing first-class lenses in F# (that is, support for lensing with F# syntax rather than with a library - tho that is a different subject for the most part).

      • Ovidiu Deac commented  ·   ·  Flag as inappropriate

        My thoughts:

        1. The property functions should be namespaced properly to avoid name collisions. For this reason I'm against the approach .PropertyName suggested below

        2. For starters doing it for record getters should be easy enough because one cannot define a record like this:

        type Point =
        x : int
        y : int

        static member x p = p.x

        // Error FS0023: The member 'x' can not be defined because the name 'x' clashes with the field 'x' in this type or module (FS0023)

        ...so if the above static member Point.x is automatically generated it shouldn't break existing code

        As Bryan Edds suggested below, this should also encourage people to use records more instead of classes.

        3. I wouldn't bother with the setters for now. The field updates could be considered as a separate feature and it probably needs more consideration.

        4. From the syntax perspective I think Point.x looks the most fsharp-ish.

        5. If you want to extend that to classes and other functions I like Python approach that member functions are just static functions which take the self instance as the first parameter. This way I the method calls below are identical in Python:

        c = MyClass()

        For me that's simple and intuitive.

        I also like Robert Gibson's suggestion to put the instance parameter last for currying reasons.

      • exercitus vir commented  ·   ·  Flag as inappropriate

        Of all the suggestions, I like Gibson's last suggestion the most.

        List.map String.length

        where String.Length is the property and String.length the corresponding function. It looks functional, plays nice with type inference and it's unambiguous (because of the lower case). Parenthesis are already too overloaded in F# for my taste.

        To avoid incompatibilities with existing code, it might even be better to indicate explicitly that this is actually an instance method turned into a static method by prefixing it with ` (accent grave) or ^ (circumflex):

        List.map String.`length


        List.map String.^length

        so that these "virtual" static methods only appear in Intellisense when you type:




        These "virtual" static methods kind of feel like the inverse of C# extension methods, which turn static methods into "virtual" instance methods.

      • Anonymous commented  ·   ·  Flag as inappropriate

        Great suggestion Gustavo - I like the idea of being able to use this shorthand:

        type Something =
        one: int
        two: int

        let ones = someSetOfSomethings |> List.map (.one)

      ← Previous 1

      F# Language

      Feedback and Knowledge Base