Skip to content

assoc for attrs? #35

@gilch

Description

@gilch

I'm thinking aloud here and looking for feedback.

This is a gap in Hy that's bothered me for a while, since I saw https://stackoverflow.com/questions/28949486/is-there-a-way-in-the-hy-language-to-use-doto-on-self

The advantage assoc has over setv is that it avoids repeating the location reference when setting multiple pairs e.g.

(setv 
  (get spam "foo") 1
  (get spam "bar") 2
  (get spam "baz") 3)

vs

(assoc spam
  "foo" 1
  "bar" 2
  "baz" 3)

It's much better than setv/get, though this is only slightly better than

(.update spam
         {"foo" 1
          "bar" 2
          "baz" 3})

it doesn't rely on the .update method being present and saves some brackets. Nice, but not essential?

But a much more common case in Python, especially when using classes, is multiple assignments to different attrs of the same object.

(defclass Point3d []
  (defn __init__[self x y z]
    (setv
      (. self x) x
      (. self y) y
      (. self z) z)))

It's the same kind of repetition. The self.x sugar helps a little, but not when the object reference is itself an expression.

But we don't have a convenient .update method, so the use case is even stronger. You could access the backing dict and do something like (.update (vars self) (vars)), but that feels a little dirty. It gives you a self.self for one thing (and any other local you've set, including hy_anon stuff and gensysms). And you can't specify different names for the attr than the parameter. We can do better:

(.update (vars self)
         {'x x  'y y  'z z})

And even better with assoc

(assoc (vars self)
  'x x  'y y  'z z))

But we still have to call vars and we still have to quote the attr names. This is probably the best we can do without introducing a new macro.

You might think you could use doto for updating an object, but it's not that easy:

(doto self
    (-> (. x) (setv x))
    (-> (. y) (setv y))
    (-> (. z) (setv z)))

doto really only works for method calls. This really isn't any easier to type. Do we want to force people to write accessors so they can use doto? (Or provide a macro to write them?) I think not, for better interop with existing Python modules. Python is not Java. doto was good enough for Clojure, when you always expect accessors. But you just can't expect that in Python.

But if we had some kind of an assoc for attrs, it would just be--

(assoc-for-attrs self
  x x   y y   z z)

Easy. We'd want a shorter name than assoc-for-attrs though.

Is there some easier way to do this I'm not seeing? Is assoc worth keeping in the first place over .update? Just to avoid some quotes and brackets? And if so, why don't we have the arguably more important assoc-for-attrs?

But maybe we could do even better than that. Parameter names often are the same as the attr names. You shouldn't have to say it twice if the names are the same. We could have a local-to-attr form,

(local-to-attr self x y z)
;; same as (setv self.x x  self.y y  self.z z)

But again, maybe with a shorter name. We could even combine the two somehow, but it would complicate the macro. Here's one possibility

(insert-better-name self .x .y  my_foo a_foo)
;; same as (setv self.x x  self.y y  self.my_foo a_foo)

Better ideas?

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions