fsprojects / FSharpPlus

Extensions for F#
https://fsprojects.github.io/FSharpPlus
Apache License 2.0
850 stars 95 forks source link

{ Idea } Dynamic lookup #74

Closed ShalokShalom closed 4 years ago

ShalokShalom commented 6 years ago

This might be also to consider: http://tomasp.net/blog/fsharp-dynamic-lookup.aspx/

^-^

gusty commented 6 years ago

We don't have any dynamic stuff at the moment, actually more on the contrary :)

But that technique might be interesting to create lens for records dynamically.

Not sure if it will make sense, if you have a concrete example would help. I will also try to come up with something.

wallymathieu commented 6 years ago

Dynamically creating lenses would be really nice. I did a project in c# to get copy update expressions (however compiling expressions often is expensive). Lenses provides the abstraction needed to make it performant (while it might be more difficult to introduce in a c# setting).

wallymathieu commented 6 years ago

Being able to do

type Bar={ qux: Qux ... }
module Bar= let qux=Lens.fromExpr <@ fun (b:Bar) -> b.qux @>
type Foo={ bar: Bar ...}
module Foo= let baz=Lens.fromExpr <@ fun (f:Foo) -> f.bar @>

Would perhaps be nice? I've not done enough lenses with f#+, but I imagine that it might look something like:

type Bar={ qux: Qux ... }
module Bar= let qux=lens (fun (b:Bar)->b.qux) (fun v (b:Bar)->{b:with qux=v })
type Foo={ bar: Bar ...}
module Foo= let bar=lens (fun (f:Foo)->b.bar) (fun v (f:Foo)->{f:with bar=v })

?

ShalokShalom commented 6 years ago

I mean in general, to provide the dynamic approach for people and cases, where they would otherwise choose a dynamic language instead.

wallymathieu commented 6 years ago

Well, since f# is an ML, you can code in it like a lisp. So you can write lispy style code. It would be nice if python, clojure or ruby was maintained for .net. The ecosystem for .net seems less diverse than for the jvm.

gusty commented 6 years ago

@wallymathieu No, I think those in your snippet are Data.Lenses.

Here we have "real" Control.Lenses, so it would be:

module Bar= let qux f { qux = q ...} = map (fun qux' -> { qux = qux' ...}) (f qux)

We should highlight in the docs the difference between Data and Control lens. Data lenses are more limited, so in Haskell they dropped using it. Control lenses are more powerful but they require functors, since this library provide Functor we are able to use Control lens.

Maybe we should also add Data lens, for completeness, for people who prefer a simple approach.

wallymathieu commented 6 years ago

Well, as long as it is documented how to use Control.Lens. My confusion about how to use the lenses in f#+ is mostly due to not having used them.

gusty commented 6 years ago

There is a good introduction at the end of the tutorial page, which in fact is a translation of the Haskell tutorial of control lens.

ShalokShalom commented 6 years ago

@wallymathieu I just learned yesterday, that Python is in full development. Clojure too?

And Java has no F#, so they can do what ever they want. I would choose .NET over it even if F# would be the only language on it.

wallymathieu commented 6 years ago

Ouu! Python3 for .net sounds nice 👍

ShalokShalom commented 6 years ago

Yeah, its good for interoperability.

wallymathieu commented 6 years ago

How is

let inline lens sa sbt afb s = sbt s <!> afb (sa s)

Used? In the above example you use what looks like something similar, but inline?

gusty commented 6 years ago

It's a function that constructs a lens from a getter and a setter, briefly explained here https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/basic-lensing#what-is-a-lens-anyhow-

So it's another way to create a lens.

Let's do a quick example, we'll create a lens for the second element of a 3-uple.

open FSharpPlus
open FSharpPlus.Lens

let getter (_, x, _) = x
let setter (a, _, c) b  = (a, b, c)

//now we can create our lens
let inline _2Of3 x = lens getter setter x

// let's read
let a = view _2Of3 (1, 2, 3)
val a : int = 2

// now write
let newTuple = set _2Of3 "two" (1, 2, 3)
// val newTuple : int * string * int = (1, "two", 3)

Btw, notice that the last line not only changes the value, it also changes the type. This wouldn't be possible with Data.Lens

wallymathieu commented 6 years ago

Aa, thanks!