feature request: NamedTuple ability to override values

118 views
Skip to first unread message

Roger Pack

unread,
Feb 22, 2017, 5:41:39 PM2/22/17
to Crystal
Currently there appears to be no way to "overwrite" a named tuple's tuple values, ex:

tuple = {x: 3, y: 4}
tuple[:x] = 3

Error in test.cr:3: undefined method '[]=' for NamedTuple(x: Int32, y: Int32)

I can understand the desire to not allow added keys to a tuple [to preserve static typed-ness] but it's sometimes convenient to change values, for instance if a tuple's value is a native or a String, it can't be changed in place it would need a setter option to ever update.

Today it seems the only way to modify the value in a named tuple would be to create a new one:

new_tuple = {x: 4, y: old_tuple[:y]}

which gets laborious for named tuples with more than a few members.

So in essence, OpenStruct was changeable values, would be nice (for instance, for those of us porting from Ruby to Crystal) to have something similar here.  Or am I missing something?
Cheers!
-roger-

Chris Hobbs

unread,
Feb 23, 2017, 6:32:03 AM2/23/17
to crysta...@googlegroups.com

Named tuples are immutable, just like tuples so their contents cannot be changed. However a nice solution to the copying verbosity problem would be a copy_with method (groovy had this for it’s @Immutable classes and it worked well). For example:

tuple = {x: 3, y: 4
}
tuple2 = tuple.copy_with(x: 5)
tuple2 # => {x: 5, y: 4}
--
You received this message because you are subscribed to the Google Groups "Crystal" group.
To unsubscribe from this group and stop receiving emails from it, send an email to crystal-lang...@googlegroups.com.
To post to this group, send email to crysta...@googlegroups.com.
Visit this group at https://groups.google.com/group/crystal-lang.
To view this discussion on the web visit https://groups.google.com/d/msgid/crystal-lang/039136fb-f310-4555-9192-d0b06d545085%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brian J. Cardiff

unread,
Feb 23, 2017, 9:03:07 AM2/23/17
to crysta...@googlegroups.com
I would chime for a `NamedTuple#merge` in the stdlib. It would be consistent with `Hash#merge`.


For more options, visit https://groups.google.com/d/optout.
--
Brian J. Cardiff
[email] bcar...@manas.tech
[web] https://manas.tech

Roger Pack

unread,
Feb 23, 2017, 11:09:40 AM2/23/17
to crysta...@googlegroups.com
On Thu, Feb 23, 2017 at 7:02 AM, Brian J. Cardiff <bcar...@manas.tech> wrote:
> I would chime for a `NamedTuple#merge` in the stdlib. It would be consistent
> with `Hash#merge`.

I'd be down with that.
[why are they immutable for values?]
> You received this message because you are subscribed to a topic in the
> Google Groups "Crystal" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/crystal-lang/YnoO8GMovKQ/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> crystal-lang...@googlegroups.com.
> To post to this group, send email to crysta...@googlegroups.com.
> Visit this group at https://groups.google.com/group/crystal-lang.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/crystal-lang/CAAvdMO-LVKhJgL-1kJQXkQrvJNkK6EY3rFLX4K-%3D5O5jQVwjqQ%40mail.gmail.com.

Chris Hobbs

unread,
Feb 23, 2017, 11:15:29 AM2/23/17
to crysta...@googlegroups.com

Because they’re stored on the stack. They’re immutable like every other struct.

Brian J. Cardiff

unread,
Feb 23, 2017, 11:25:31 AM2/23/17
to crysta...@googlegroups.com
Chris, 

Structs are value types, but they are mutable if their api allow it.
Tuples and NamedTuples are inmutable by design.


For more options, visit https://groups.google.com/d/optout.

Chris Hobbs

unread,
Feb 23, 2017, 11:28:56 AM2/23/17
to crysta...@googlegroups.com

I realise, however keeping the “structs must be immutable” perception alive, we avoid a bunch of problems with people not understanding structs later down the line. Even the documentation suggests that you make structs immutable:

Of course, this is different for lib structs.

Yawar Amin

unread,
Feb 24, 2017, 1:35:12 PM2/24/17
to Crystal
This is nice; we have this immutable (or functional) update technique in most languages with immutable record types--ML, Haskell, Scala, Rust etc. Suppose we call it NamedTuple#update; then we could do stuff like:

tuple = { x: 3, y: "Hello", z: false }
tupl2 = tuple.update x: 4
tupl3 = tupl2.update y: "Goodbye", z: true

So this looks like it needs a certain amount of dynamic generation of update method overloads. I assume Crystal can do that with a macro...

Cheers,

Yawar

Need47

unread,
Feb 24, 2017, 1:44:34 PM2/24/17
to crysta...@googlegroups.com
This is what I learned from crystal-lang on GitHub.

cheers
Tiejun

# open NamedTuple
#
struct NamedTuple
  # create a new `NamedTuple` by merging with *another* `NamedTuple`
  #
  # NOTE: values of duplicate keys will be override by *another*
  # ```
  # a = {x: 1, y: 2}
  # b = {z: 3, y: 0}
  # p a.merge(b) # => {x: 1, y: 0, z: 3}
  # ```
  def merge(other : NamedTuple)
    merge_implementation(other)
  end

  private def merge_implementation(other : U) forall U
    {% begin %}
      NamedTuple.new(
        {% for key in (T.keys + U.keys).uniq %}
          {% if U.keys.includes?(key) %}
            {{key}}: other[:{{key}}],
          {% else %}
            {{key}}: self[:{{key}}],
          {% end %}
        {% end %}
      )
    {% end %}
  end # merge_implementation()
end   # NamedTuple

To unsubscribe from this group and stop receiving emails from it, send an email to crystal-lang+unsubscribe@googlegroups.com.

To post to this group, send email to crysta...@googlegroups.com.
Visit this group at https://groups.google.com/group/crystal-lang.

For more options, visit https://groups.google.com/d/optout.



--
-- Tiejun
提问之前请先查阅《CN-Rockville生活手册》相关经验分享——http://goo.gl/WDl5H

Roger Pack

unread,
Feb 24, 2017, 2:05:21 PM2/24/17
to Crystal


On Thursday, February 23, 2017 at 9:28:56 AM UTC-7, RX14 wrote:

I realise, however keeping the “structs must be immutable” perception alive, we avoid a bunch of problems with people not understanding structs later down the line. Even the documentation suggests that you make structs immutable:


How are Value's allocated on the stack if I can make an array of NamedTuples and pass that array out of a method?  I assume the struct is duplicated when placed into the Array and then just a reference to the Array returned?  That explains why the Immutable aspect (otherwise people will make copies on accident, then wonder "wait why is this only changing locally").

If that's the case, my next question would be "why have NamedTuple a Value type [like Structs are]?"  Named tuple "like" syntax for objects in javascript (ex: {a: 3}) are mutable, OpenStruct is mutable...
NamedTuple itself is so useful to "general" programming (in essence, a replacement for a Hash but that is now strongly typed) that it would make sense for it to be of a more general utility... 
Docs for struct: "A struct is mostly used for performance reasons to avoid lots of small memory allocations when passing small copies might be more efficient." 
In my head immutable Value's are useful for specific cases but could cause confusion when used as general tools...which is this case.  I would guess that most people don't think "I want to use NamedTuples to reduce the amount of copying" it's for the nice syntax that they are most useful...Or am I missing something else?
Thanks :)
-roger-

Yawar Amin

unread,
Feb 24, 2017, 11:32:47 PM2/24/17
to Crystal
Cool. I just realised it can be even simpler, e.g.:

struct Person
  getter id, name

  def initialize(@id : Int64, @name : String)
  end

  def update(id = @id, name = @name)
    @id = id
    @name = name
  end
end

We can probably capture this pattern (update each of the getters) in a macro.
Reply all
Reply to author
Forward
0 new messages