Giter VIP home page Giter VIP logo

aether's People

Contributors

kolektiv avatar mexx avatar neoeinstein avatar njlr avatar panesofglass avatar tachyus-ryan avatar varon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aether's Issues

The `^=` operator seems backwards, and I'm not sure about the `^%` operator

With Optic.set, the parameters are in the order I would expect. But the ^= operator has the parameters backwards from Optic.set, which is not what I would expect. As for Optic.map and the ^% operator, I'm not 100% sure which way around I'd expect the parameters to be, but I feel like they should at least be consistent between ^% and Optic.map. Let me use an example to show you what I mean:

type Data = { items: int list; code: string }
let items_ : Lens<_,_> = (fun d -> d.items), (fun l d -> { d with items = l })
let data = { items = [1;2;3]; code = "foo" }
data |> Optic.get items_  // This feels "natural"
data ^. items_  // As does this
data |> Optic.set items_ [4;5;6]  // As does this
data |> ([4;5;6] ^= items_ ) // But this feels wrong
data |> (items_ ^= [4;5;6])  // This would feel more natural
data |> Optic.map items_ (List.map (*2)) // Whereas this feels "backwards"...
data |> (List.map (*2) ^% items_) // ... and this feels "natural"

It's probably too late to change this for Aether 8.x, but 9.x isn't released yet, right? At least I don't see a NuGet package for 9.0. I'd love to see the ^= operator feel a bit more natural to use (even if it won't ever be really natural -- it "wants" to be a custom ternary operator, and F# makes it hard to define those).

Update tests from deprecated .NET Core 2.2 to .NET 6

Is your feature related to a problem? Please describe.
Currently, the tests are still on .NET Core 2.2, which is not supported anymore. .NET Core in general is only supported till the end of this year.

What solution do you propose?
We should consider moving towards .NET 6 support.

Thx for taking this into consideration. Super great library! For me, even the best lenses library for F# out there.

How to write a lens for grandchildren by ID when you don't know the child ID

I have the following type definitions in F#. How can I create a lens from Parent to a given GrandChild (identifier by ID)?

type GrandChild = { Id: int }

type Child = { Id: ChildId; Children: GrandChild list }

type Parent = { Children: Map<ChildId, Child> }

Here is my ugly attempt, which seems like just the kind of thing lenses is used to avoid:

module Parent =

  let grandChild grandChildId : Lens<Parent, GrandChild> =
    (fun x ->
      x.Children
      |> Map.toList
      |> List.collect (fun (_, c) -> c.GrandChildren)
      |> List.find (fun gc -> gc.Id = grandChildId)
    ),
    (fun v x ->
      {
        x with
          Children =
            x.Children
            |> Map.map (fun _ c ->
                { c with
                    GrandChildren =
                      c.GrandChildren
                      |> List.map (fun gc -> if gc.Id = grandChildId then v else gc) }
            )
      }
    )

I'm not even sure this follows the optic laws.

Part of my challenge here is that there is no correspondence between the child IDs and the grandchild IDs. When I'm at the parent level and want to update a specific GrandChild, I have to check all Children and all GrandChildren.

I assume that there is an easier and more composable way of doing this, but it's escaping me.

If it would simplify anything, you can assume that the grandchild specified by grandChildId is guaranteed to exist in the hierarchy (hence the use of Lens instead of Prism).

Change Lens type from Tuple to a record/union

A great package, I'm enjoying it thoroughly, with the exception of compile errors.

It quite often happens the type checker substitutes the type alias for the actual type (espcially in the case of an inferred type and especially in the case of an error) and produces errors more difficult to parse than they would be if the lens was stored not as a tuple but rather a record:

expected: Lens<MyRecord, int>
vs
expected: (MyRecord -> int) * (int -> MyRecord -> MyRecord)

Which needless to say gets worse with the complication of either of the types involved.

Would this be something you'd be willing to consider changing or is it too compatibility breaking to be worth thinking about?

Lenses on Generic Records

Are there any suggested practices for declaring lenses on generic records?

type Record<'a> = 
    { Data : 'a }
    static member data_ = (fun x -> x.Data), (fun v x -> { x with Data = v })

In this case, data_ is inferred to have type (Record<obj> -> obj) * (obj -> Record<obj> -> Record<obj>) aka Lens<Record<obj>, obj>.

Placing it as a module function without any explicit types sort of works:

module Record =
    let data_ = (fun x -> x.Data), (fun v x -> { x with Data = v })

but it infers a too general type of (Record<'a> -> 'a) * ('b -> Record<'b> -> Record<'b>) rather than the lens type of (Record<'a> -> 'a) * ('a -> Record<'a> -> Record<'a>).

It's possible to explicitly provide the types:

type Record<'a> = 
    { Data : 'a }
    static member data_: Lens<Record<'a>,'a> = (fun x -> x.Data), (fun v x -> { x with Data = v })

But obviously with longer signatures inside records this becomes incredibly unwieldy, as per this example from my own project:

let properties_ : Lens<StructureDescription<'a>, (string * Parser<'a -> 'a, unit>) list> = (fun x -> x.properties), (fun v x -> { x with properties = v })

What's the best way of declaring these?

Nuget package for .Net 3.5

Having support for Aether on the .Net 3.5 platform would be very useful.

I've tested the compilation and that works correctly, but it seems as though Aether does not provide a Nuget package for this version.

Lens Creation

Is there any additional documentation on defining lenses?

is it possible to automatically create them for a given record, or do we need to manually define them in long-hand for each field?

Would it be possible to include this in the documentation directly?

Add Optic.over to update a value

FSharpPlus has an over function to update a value for a lens. I find this very useful, e.g. for appending an element to a list targeted by an optic.

For Aether, it can be defined as simply as this:

let inline over optic update source =
  Optic.set optic (update (Optic.get optic source)) source

I have this helper defined in my own prelude, but it would be great to have this in Aether.

Sign the aether assemblies with a strong name

Have you considered signing your assemblies with a strong name?

The problem with not signing your assemblies is that you alienate the inclusion of your library in a project that is strongly named. I can get around this temporarily, but I have to actually sign your dlls with my own key. This is a bit of a hassle, especially if I want to release my work as an open source project.

Usage question: default value for lens to option?

Suppose I have a map like this:

type Employee = 
  {
    JobTitle : string
    Salary : int
  }
  with 
    static member Salary_ = 
      (fun x -> x.Salary), (fun v x -> { x with Salary = v })

let employees = 
  Map.empty
  |> Map.add "alice" { JobTitle = "Manager"; Salary = 86 }
  |> Map.add "bob" { JobTitle = "Customer Support"; Salary = 76 }

I want to create an optic for updating the salaries. Something like:

let myLens name = 
  Map.value_ name >-> Employee.Salary_

However, this optic is Lens<Map<string, Employee>, int option>.

Instead, I would like a Lens<Map<string, Employee>, int>, by providing a default int value.

Something like:

// Not real code
let myLens name = 
  Map.value_ name >-> Employee.Salary_ >-> Lens.defaultValue 0

How should I do this?


Here is what I came up with, but I have a feeling this is already built into the library:

let composeOption (b : Lens<'b, 'c>) (a : Lens<'a, 'b option>) : Lens<'a, 'c option> =
  let getA, setA = a
  let getB, setB = b

  let get = (fun x -> getA x |> Option.map getB)
  let set = (fun (v : 'c option) (x : 'a) ->
    match getA x with
    | Some b ->
      match v with
      | Some c -> x |> setA (setB c b |> Some)
      | None -> x
    | None -> x
  )

  get, set

Convention for lens declarations

Is there any convention for declaring lenses?

Considering the following example:

type Foo = 
    { aValue : int
      bValue : int }

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Foo =
    module Lens =
        let aValue:Lens<_,_> =  (fun foo -> foo.aValue), (fun aValue x -> { x with aValue = aValue })
        let bValue:Lens<_,_> =  (fun foo -> foo.bValue), (fun bValue x -> { x with bValue = bValue })

Several questions arise:

  1. Do we want to put the lenses into a Lens submodule?
  2. Do we suffix them with 'Lens'? (I've seen aValue_ in the Foo module before, but that seems a bit opaque.)
  3. Do we use the shorthand v and x ? (The names are hidden as they're lambdas anyway the Lens<_,_>)

Having some conventions here might make these more discoverable, at least until they're introduced as a first class language feature.

Simplified documentation example?

In the Composition section of the Lenses guide, there's this example:

(* Lens<'a,'b> -> 'a -> 'b *)
let get (g,_) =
    fun a -> g a

(* Lens<'a,'b> -> 'b -> 'a -> 'a *)
let set (_,s) =
    fun b a -> s b a

Could (and should?) this be simplified to:

(* Lens<'a,'b> -> 'a -> 'b *)
let get (g,_) = g

(* Lens<'a,'b> -> 'b -> 'a -> 'a *)
let set (_,s) = s

The latter code snippet is functionally the same as the former. Is there any reason why the first code snippet is used instead of the second? Perhaps I'm missing the point it's trying to make.

Remove RequireQualifiedAccess from Optic module

I'd like to open Optic so I can just do x |> set foo_ "bar" instead of x |> Optic.set foo_ "bar", but Optic is marked with RequireQualifiedAccessAttribute.

This may be relevant for other modules as well; haven't checked.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.