@OvermindDL1: I'm still grokking what you've proposed, but it's problematic
for a few reasons. I'm 99% sure that it can't be done in a way that is type
safe. What you're doing doesn't need Higher-Kinded types, it's just not
type safe, at least not as how it's presented here.

One is that you've got type variables in your Access type that it is not
parameterized over. So you need to do something like this:

type Access recordKeyType  dictKeyType
  = All
  | At Int
  | Elem Int
  | Key recordKeyType {- Whatever recordKeyType might be as an indicator
for a key on a record -}
  | DictKey dictKeyType
  | Fn (EnumerableType -> EnumerableType) {- This is why I think HKT's
might be needed, or special caseing in the compiler -}

or, with Higher-Ranked Types

type Access
  = All
  | At Int
  | Elem Int
  | Key (forall recordKeyType . recordKeyType) {- Whatever recordKeyType
might be as an indicator for a key on a record -}
  | DictKey (forall dictKeyType . dictKeyType)
  | Fn (EnumerableType -> EnumerableType) {- This is why I think HKT's
might be needed, or special caseing in the compiler -}

but, I'm pretty sure neither of these would do what you want. In the first
case, you'd need to know ahead of time what kind of record or Dict you were
dealing with, or you'd need to write something that worked with any
record/dict type, which is probably too generic to get anything done.

In the second case, you'd have to give a value that is *truly* generic,
that matches literally any type you throw at it. There is no such value.
This is also problematic, since it causes all sorts of awful with type
inference and would require significantly changing Elm's type system.

Also, I don't think you're quite understanding how the type variables work.
Let's look at the signature here:

{-| Sets a value(s) at the given path -}
putIn
  : List Access
  -> newValue
  -> EnumerableType
  -> EnumerableType

Every type variable in Elm has an implicit "forall" at the beginning of the
signature. So this is really:

{-| Sets a value(s) at the given path -}
putIn
  : forall newValue . List Access
  -> newValue
  -> EnumerableType
  -> EnumerableType

so what you're saying is, for *literally any type* T, if you give me a List
of Access and a value of type T and an EnumerableType, I can give you an
updated EnumerableType. So all of the following would be allowed:

    putIn [At 3] "Hello" {field = False}
    putIn [Key foo] "Hello" {foo = True}
    putIn [Key foo] "Hello" {bar ="Goodbye"}

All of these should generate type errors, but they wouldn't with the given
types you have.

On Sun, Aug 14, 2016 at 8:40 AM, OvermindDL1 <[email protected]> wrote:

> Expressions are not currently allowed to be updated like that, so would
> that return a model with the something.more updated on it (seems
> surprising) or return model.something with more updated on it (not
> surprising, and would allow arbitrary expressions there).
>
>
>
> On Saturday, August 13, 2016 at 11:51:29 PM UTC-6, Robin Heggelund Hansen
> wrote:
>>
>> All I really want is:
>>
>> ```elm
>> { model.something |  more = 42 }
>> ```
>>
>> søndag 14. august 2016 02.49.07 UTC+2 skrev OvermindDL1 følgende:
>>>
>>> Just a passing idea to perhaps help give ideas for better methods:
>>>
>>>
>>> Updating a nested record is a bit convoluted as something like:
>>> ```elm
>>> let
>>>   something = model.something
>>> in
>>>   { model | something = { something | more = 42 } }
>>> ```
>>> Excepting the let/in part because Elm does not support an expression as
>>> the first argument (`model` and `something` in these cases) for
>>> I-have-no-clue-reason, and another language I work often in is Elixir, its
>>> syntax for the above would be similar:
>>> ```elixir
>>>   %{ model | something: %{ model.something | more: 42 } }
>>> ```
>>>
>>> However, that is painful, so Elixir has a couple of helper functions
>>> that simplify that kind of work, let me demonstrate, this does the same as
>>> the above:
>>> ```elixir
>>>   put_in models, [:something, :more], 42
>>> ```
>>> And you can go arbitrarily deep and it returns a new model with the path
>>> altered to the given value as necessary.  Elixir also has lispy macros so
>>> you can also use the above function via:
>>> ```elixir
>>>   put_in models.something.more, 42
>>> ```
>>> Basically using 'read' syntax to specify the path, but it gets expanded
>>> to the above at compile-time.  It also supports not only records but also
>>> maps (dicts in elm), lists (also lists in elm) and anything else that
>>> follows the Access protocol (a set of functions of certain types to do
>>> basic functions), but those are the default.
>>>
>>> It has extra features like this, say `model.something` is a `List Int`
>>> in elm parlance:
>>> ```elixir
>>> put_in model, [:something, Access.all], 42
>>> ```
>>> This will set any and all values in the list at model.something to 42,
>>> not terribly useful, however it has a lot more functions as well, such as
>>> (I want to use more 'elmy' syntax, so I will now use things like
>>> `.something` instead of `:something` and no commas between arguments, only
>>> in tuples and lists and such):
>>> ```elixir
>>> -- Where model = { something : Dict String (List Int) }
>>> ( oldValue, newModel ) = get_and_update_in model [ .something, "joe",
>>> Access.at(0) ] (\oldValue -> let oldValue = Maybe.withDefault 0 in (
>>> oldValue, Just (oldValue+1) ))
>>> ```
>>> This will update a value in and let you return a value (anything you
>>> wish) within a tuple.  This one will access `Dict.get "joe"
>>> model.something` and get the returned list, accessing the first element
>>> (`at` for lists, `elem` for a tuple index starting at 0 as well), and the
>>> called passed in function returns a tuple where the first element is the
>>> first element of the returned tuple and the second element is what the
>>> thing at the path will be updated to, so this case will return the
>>> `oldValue+1` if it existed, if it did not then it returns 1 due to the
>>> `withDefault 0`.
>>>
>>> More functions it adds are:
>>> ```elixir
>>> -- Where model = { something : Dict String (List Int) }
>>> value = get_in model [ .something, "joe", Access.at(2) ] -- Returns the
>>> value at the path
>>>
>>> values = get_in model [ .something, Access.all, Access.at(2) ] --
>>> Returns all of the values 2nd values in the lists in all the values of the
>>> dictionary as a list if they exist, else they are skipped
>>>
>>> pop_in model [ .something, Access.all, Access.at(2) ] -- Removes the
>>> element in the list at position 2 in all the dictionary values if it
>>> exists, if it does not exist then it skips it
>>>
>>> update_in model [ .something, Access.all, Access.at(2) ] (\oldValue ->
>>> Just (( oldValue |> Maybe.withDefault 0 ) + 4)) -- Updates a value(s)
>>> in-place
>>> ```
>>> Along with macro's for the read-format pathing, which is not needed here.
>>>
>>> The keylist (the `[ .something, Access.all, Access.at(2) ]` in the last
>>> example) can also take functions, whatever they return (empty list,
>>> single-element list, multiple-element list, etc...) will be what is used
>>> and what is set back.
>>>
>>>
>>> *Thus*, what would be thought of Elm adding in functions like these
>>> (HKT's might be needed, not thought through the implementation yet, only
>>> the API):
>>> ```
>>> type Access
>>>   = All
>>>   | At Int
>>>   | Elem Int
>>>   | Key recordKeyType {- Whatever recordKeyType might be as an indicator
>>> for a key on a record -}
>>>   | DictKey dictKeyType
>>>   | Fn (EnumerableType -> EnumerableType) {- This is why I think HKT's
>>> might be needed, or special caseing in the compiler -}
>>>
>>> -- You'd need some kind of EnumerableType as well, no doubt opaque or
>>> something, or need HKT's, probably need HKT's in general, Elm really badly
>>> needs HKT's...
>>>
>>> {-| Get a value calculated from the old value and set a new value
>>> simultaneously -}
>>> getAndUpdateIn
>>>   : List Access
>>>   -> (Maybe valueType -> ( retValue, Maybe valueType ))
>>>   -> EnumerableType
>>>   -> ( List retValue, EnumerableType )
>>>
>>>
>>> {-| Gets a value from an access path -}
>>> getIn
>>>   : List Access
>>>   -> EnumerableType
>>>   -> List retValue
>>>
>>>
>>> {-| Removes a value from a given path if possible, returning it if it
>>> exists -}
>>> popIn
>>>   : List Access
>>>   -> EnumerableType
>>>   -> ( List retValue, EnumerableType )
>>>
>>>
>>> {-| Sets a value(s) at the given path -}
>>> putIn
>>>   : List Access
>>>   -> newValue
>>>   -> EnumerableType
>>>   -> EnumerableType
>>>
>>>
>>> {-| Updates a value in the path and returns the new modified object -}
>>> updateIn
>>>   : List Access
>>>   -> (Maybe oldValue -> Maybe newValue )
>>>   -> EnumerableType
>>>   -> EnumerableType
>>> ```
>>>
>>> These could then be used like:
>>> ```
>>> -- Where model = { something : Dict String (List Int) }
>>> -- With values of:  model = { something = Dict.fromList [("joe", [1, 2,
>>> 3]), ("beth", [10, 11, 12, 13])] }
>>>
>>>
>>> ( oldValue, newModel ) = model |> getAndUpdateIn [ Key .something,
>>> DictKey "joe", All] (\v -> ( v, v |> Maybe.withDefault 0 |> (+) 1 ))
>>> -- Will return `oldValue == 1`
>>> -- And returns `newModel == { something = Dict.fromList [("joe", [2, 2,
>>> 3]), ("beth", [10, 11, 12, 13])] }`
>>>
>>>
>>> newModel = model |> putIn [ Key .something, All ] [ 42 ]
>>> -- Will return `newModel == { something = Dict.fromList [("joe", [42]),
>>> ("beth", [42])] }````
>>>
>>>
>>> newModel = model |> updateIn [ Key .something, Fn (\dict -> dict |>
>>> Dict.filter (\k v -> (String.length k) >= 4 ) ) ] (\v -> v)
>>> -- Will return `newModel == { something = Dict.fromList [("beth", [10,
>>> 11, 12, 13])] }`
>>> -- This is because we used a Fn to return the set of things we want to
>>> operate over and as such will only assign those back, allowing us to filter
>>> out things with ease.
>>> ```
>>>
>>> This style makes doing very complex embedded enumerable updating with
>>> ease.  However as the above is proposed it would likely require Higher Kind
>>> Types in Elm, which does not have those yet, thus for now just implementing
>>> the above for just records would be sufficient for a good portion if not
>>> the overwhelming majority of program, and that could be done without HKT's.
>>>
>> --
> You received this message because you are subscribed to the Google Groups
> "Elm Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups "Elm 
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to