It's not a huge deal but other strongly typed languages definitely allow
arbitrary expressions in record update syntax.
# type t = { a : int ; b : float } ;;
type t = { a : int; b : float; }
# let emptyT () = { a = 1 ; b = 3.0 } ;;
val emptyT : unit -> t = <fun>
# let x = { (emptyT ()) with a = 7 } ;;
val x : t = {a = 7; b = 3.}
On Saturday, March 18, 2017 at 10:26:57 AM UTC-7, Mark Hamburg wrote:
>
> I just had a senior engineer on my team ask me whether it was really the
> case that putting a qualified name or a field access into the beginning of
> a record update would fail to compile. His examples were much like the
> other examples from this thread, but I'm noting this here to make clear
> that this seems to be a routine stumbling block.
>
> Mark
>
> On Mar 9, 2017, at 3:09 PM, Mark Hamburg <[email protected] <javascript:>>
> wrote:
>
> I got asked for non obfuscated/simplified examples, so let me run through
> some more realistic code. I would pull from our own codebase but we've
> often contorted ourselves away from these patterns because they are so ugly
> so I can't just grab existing code.
>
> Imagine having a style element that includes information about padding on
> each side:
>
> type Rect = { left : Int, top : Int, right : Int, bottom : Int }
>
> type Style = { padding : Rect, ... }
>
>
> Expressing it this way allows us to work with the padding as a whole when
> useful and to avoid having to work with field names like leftPadding,
> topPadding, etc (presumably along with leftMargin, topMargin, etc)
>
> But now say one wants to change just the left padding. The code has to
> build a new padding object and then stick it back into the style. A little
> ugly from the standpoint of repetition, but maybe just...
>
> { style | padding = { style.padding | left = 10 } }
>
>
> Nope. We can't use an expression for the record to be updated. So, instead
> we have to write something more like:
>
> let
> oldPadding =
> style.padding
> newPadding =
> { oldPadding | left = 10 }
> in
> { style | padding = newPadding }
>
>
> Or we can use the successive wrapping pattern if we have the necessary
> utility functions:
>
> 10 |> asLeftIn style.padding |> asPaddingIn style
>
>
> And the inability to use an expression to derive the record also means
> that we can't do something like:
>
> { Style.default | color = "red" }
>
> But rather have to write:
>
>
> defaultStyle : Style.Style
> defaultStyle = Style.default
>
> ...
>
> { defaultStyle | color = "red" }
>
> I don't have a good answer to the ugliness of the nested update though the
> successive wrapping pattern would probably be pretty reasonable if we had
> .field= in addition to .field. But the latter problem seems like the sort
> of thing that should be very straightforward to solve and would also allow
> for writing the first version of updating the left padding that I wrote
> above.
>
> Mark
>
> On Fri, Mar 3, 2017 at 8:44 AM, Mark Hamburg <[email protected]
> <javascript:>> wrote:
>
>> Our codebase suffers from this as well. And unlike what one of the follow
>> ups noted, this isn't an issue of wanting to add fields. Rather, it's an
>> issue of not being able to use an expression in the first part of the
>> record update. In this case, one doesn't even need a general expression but
>> just an imported value. On the their hand, members of my team have gotten
>> bit by and complained about not being able to construct a nested update
>> using what they thought were the language constructs:
>>
>> newFoo = { oldFoo | nested = { oldFoo.nested | field = 3 } }
>>
>> The "best" syntax I've seen for that is something like:
>>
>> newFoo =
>> 3 |> asFieldIn oldFoo.nested |> asNestedIn oldFoo
>>
>> But that's pretty obscure as well.
>>
>> That said, I just added a utility module to our project that is filled
>> with generic setFoo and asFooIn functions for every field name that seems
>> at all likely to be used more than once. I would love to have support for
>> .field= functions to parallel the .field functions. I would have them take
>> the assigned value as a first parameter and the record as a second
>> parameter to enable writing:
>>
>> Slides.defaultConfig
>> |> .someAttribute= myCustomValue
>>
>> The case where the parameters are reversed which helps with nesting could
>> be handled using a utility function:
>>
>> 3
>> |> into oldFoo.nested .field=
>> |> into oldFoo .nested=
>>
>> Mark
>>
>> On Mar 2, 2017, at 10:16 PM, Richard Feldman <[email protected]
>> <javascript:>> wrote:
>>
>> Re-posting the first example from Franscisco's thread
>> <https://groups.google.com/forum/#!topic/elm-discuss/vzcrRdgY1-w>:
>>
>> There is a common pattern where a library (ex, elm-markdown) will provide
>> a default config, to be extended by the user.
>>
>> Here the two ways to do this right now, one painfully verbose and the
>> other relying on exposing values that would read better if fully qualified
>> instead of exposed:
>>
>> import Slides
>>
>> slidesDefaultConfig =
>> Slides.defaultConfig
>>
>> myCustomSlidesConfig =
>> { slidesDefaultConfig | someAttribute = myCustomvalue }
>>
>> or
>>
>> import Slides exposing (slidesDefaultConfig)
>>
>> myCustomSlidesConfig =
>> { slidesDefaultConfig | someAttribute = myCustomvalue }
>>
>> Not a big deal TBH, but annoying.
>>
>>
>> --
>> 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] <javascript:>.
>> 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.