Re: [go-nuts] A proposal: Simplification of `map[string]interface{}` with Assumptive Maps

558 views
Skip to first unread message

Rémy Oudompheng

unread,
Jun 5, 2013, 2:46:55 AM6/5/13
to m...@stretchr.com, golang-nuts
On 2013/6/5 <m...@stretchr.com> wrote:
> A proposal: Simplification of `map[string]interface{}` with Assumptive Maps
>
> We write a lot of Go code, and most of it seems to be
> `map[string]interface{}`. Therefore we propose a new language feature
> called "Assumptive Maps".

I'm not sure where you get that name "assumptive maps" from.

The proposal would seriously modify the meaning of the following code:
(http://play.golang.org/p/ACaNb0k0ab)

package main

import "fmt"

var label = "hello"

func main() {
world := "world"
{label: fmt.Sprint("value: %s", world)}
}

> What do you think?
> We would very much appreciate any feedback or thoughts regarding this idea,
> and would be prepared to contribute code to make it happen if the people
> want it.

The idea seems very dificult to accept because of what I said.
Note however, that in virtue of the "type elision in composite
literal" feature of Go 1, the following code is legal and work as you
would expect:

http://play.golang.org/p/9T3nRT0rzO

package main

import "fmt"

var label = "hello"

func main() {
world := "world"
s := []map[string]string{
{label: fmt.Sprintf("value: %s", world)},
}
fmt.Printf("%+v", s)
}


Rémy.

Jesse McNelis

unread,
Jun 5, 2013, 2:48:24 AM6/5/13
to m...@stretchr.com, golang-nuts
On Wed, Jun 5, 2013 at 2:44 PM, <m...@stretchr.com> wrote:
>
> A proposal: Simplification of `map[string]interface{}` with Assumptive Maps
>
> We write a lot of Go code, and most of it seems to be `map[string]interface{}`. Therefore we propose a new language feature called "Assumptive Maps".

If most of your Go code is map[string]interface{} then you're doing
something wrong.
It sounds like you're trying to write Javascript in Go. This is a bad idea.
Go is a statically typed language, use this to your advantage instead
of trying to fight it.


--
=====================
http://jessta.id.au

Kevin Gillette

unread,
Jun 5, 2013, 3:50:27 AM6/5/13
to golan...@googlegroups.com, m...@stretchr.com
Assuming that it will determine the value (and perhaps key) types based on the observed "assumptive literal", it'd be really easy to break code with minor changes. For example:

== Before ==

m := {1: 2, 3: 4}
var x map[int]int = m // works

== After ==
m := {1: 2, 3: 4.5}
var x map[int]int = m // broken, since m is now inferred to be either map[int]interface{} or map[int]float64, depending on defined semantics.

On Tuesday, June 4, 2013 10:44:05 PM UTC-6, m...@stretchr.com wrote:
A proposal: Simplification of `map[string]interface{}` with Assumptive Maps

We write a lot of Go code, and most of it seems to be `map[string]interface{}`.  Therefore we propose a new language feature called "Assumptive Maps". 

What do they look like?
Instead of

  • mymap := map[string]string{"Name":"Mat"}
and
  • var mymap map[int]string := map[int]string{1: "Mat", 2: "Tyler"}

we could write:

  • mymap1 := {"Name": "Mat"}
  • /* mymap1 would be of type map[string]interface{} or 
  •    map[string]string depending on the options below */

  • var mymap2 map[int]string = {1: "Mat", 2: "Tyler"}
  • /* mymap2 would be of type map[int]string - since the developer
  •    has explicitally assigned a type to the lvalue */

What are they?
An assumptive map is a compile-time shortcut for defining a map. At compile time, the type of the map is determined by examining the values assigned to the map.

Selecting types

  • Option one
  • The types can be determined by the content of the shortcut map.  For example, if all keys are string we know it'll be `map[string]...`.  If all values are `int` the compiler knows it will be `map[string]int`

  • The compiler would throw an error if there are type mismatches, and force the developer to make a decision.

  • Option two
  • Another option, is that everything defined with `{...}` assumptive syntax always becomes `map[string]interface{}`, unless the lvalue already has a type defined.

  • This provides complete consistency and means that maps won't change type depending on the data.  If the user were to pass a value of a type other than string as the key, the compiler would issue an error.

  • However, if the user already has a variable with a specific type of map, the `{...}` syntax would use that type instead of just assuming `map[string]interface{}`.

  • var mymap map[int]string
  • mymap = {1: "Mat", 2: "Tyler"}
  • /* this is OK */

  • If the user were to specify a value of a type other than one defined (or assumed) as the key, the compiler would issue an error.

  • mymap := {1: "Mat", 2: "Tyler"}
  • /* this would be an error, because map[string]interface{} is
  •    assumed by default */
  •    
Pros

  • Easier to type and read
  • Familiar; especially since it's so similar to JSON
  •    
Cons

  • The code is potentially unclear and would require outside knowledge of the feature
  •    
Other ideas

  • Something similar could be done for arrays of maps;  `[{"name":"Mat"},{"name":"Tyler"}]`

roger peppe

unread,
Jun 5, 2013, 6:05:30 PM6/5/13
to Kevin, m...@stretchr.com, golang-nuts

One trick I've used in the past to save typing in this kind of situation is to define a type.
Eg type m map[string]interface{}

Then you can write m{"foo": 5} and or is assignment-compatible with map[string]interface{}.

It's not flawless (when they're nested, something might be confused by the m type), but it's a useful technique and good in other situations too.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Mat Ryer

unread,
Jun 5, 2013, 1:42:31 PM6/5/13
to Kevin Gillette, golan...@googlegroups.com
Kevin, 

You're right.  I actually prefer the `{…}` being a shortcut for `map[string]interface{}` option.  Which would insist the keys are strings, but the values could be anything.

Mat

David DENG

unread,
Jun 6, 2013, 12:09:16 PM6/6/13
to golan...@googlegroups.com, m...@stretchr.com
I may suggest a more conservative type deduction as follow:

for the following assignment:

m := {a: b, c: d}

the rule for the map is simple and clear: the type of the key is the type of a, as if the deductive type for key := a. If the type for c is different, an error raised. Similar rule for type of the value.

map[string]interface{} should be used rarely.

David

Gustavo Niemeyer

unread,
Jun 6, 2013, 12:27:35 PM6/6/13
to m...@stretchr.com, golan...@googlegroups.com
On Wed, Jun 5, 2013 at 1:44 AM, <m...@stretchr.com> wrote:
> A proposal: Simplification of `map[string]interface{}` with Assumptive Maps
>
> We write a lot of Go code, and most of it seems to be
> `map[string]interface{}`.

This is definitely not true, at least for the code I read and write.

That said, the idea of shortening composite literals further has
already received serious attention from the core development team, and
failed:

https://codereview.appspot.com/5449071/


gustavo @ http://niemeyer.net

Mat Ryer

unread,
Jun 6, 2013, 1:05:32 PM6/6/13
to Gustavo Niemeyer, golan...@googlegroups.com
Thanks for the link.

Jason Moiron

unread,
Jun 6, 2013, 2:45:14 PM6/6/13
to golan...@googlegroups.com, m...@stretchr.com
When I first started writing Go code, I did this a lot:

type dict map[string]interface{}

mgo/bson does this too, for instance:


These days, I tend to stay away from maps except when absolutely necessary.  Still, there are problem domains where lots of them are absolutely necessary, and I see no problem with creating this kind of shortcut type alias.  There are a lot of drawbacks to relying on `map[string]interface{}`, but sometimes you really have lots of nails lying around and you really do need a hammer.

All that being said, I don't think there is a set of assumptions/behaviors which is both simple enough and useful enough for type inference on map literals.

-- Jason

On Wednesday, June 5, 2013 12:44:05 AM UTC-4, m...@stretchr.com wrote:
A proposal: Simplification of `map[string]interface{}` with Assumptive Maps

Kevin Gillette

unread,
Jun 6, 2013, 3:08:53 PM6/6/13
to golan...@googlegroups.com, Kevin Gillette, m...@stretchr.com
In that case, rog's suggestion is equivalent to yours, but can be done now, and only costs an extra byte -- e.g. m{"a": 1, "b": 2} instead of {"a": 1, "b": 2}

Mat Ryer

unread,
Jun 6, 2013, 6:23:51 PM6/6/13
to Kevin Gillette, golan...@googlegroups.com
I have tried this before, and didn't like the fact that I had to always cast to my fake 'm' type.  Which, outside of the package would end up being packagename.M.  You can see how verbose it becomes quite quickly.

Jan Mercl

unread,
Jun 7, 2013, 4:36:35 AM6/7/13
to m...@stretchr.com, golang-nuts
On Wed, Jun 5, 2013 at 6:44 AM, <m...@stretchr.com> wrote:
> A proposal: Simplification of `map[string]interface{}` with Assumptive Maps

I don't like the idea. Consider this valid code:
(http://play.golang.org/p/rJik34-EdT)

package main

import "fmt"

func main() {
mmmmmm := map[int]string{1: "foo", 42: "bar"}
m := map[int]interface{}{1: "foo", 42: "bar"}
ssssssssss := [50]string{1: "foo", 42: "bar"}

fmt.Println(mmmmmm)
fmt.Println(m)
fmt.Println(ssssssssss)
}

Which type, of the three above, would `qux := {1: "foo", 42: "bar"}`
be in your proposal?
Why that one?
How the other two would have to be written then? Only using the long/full form?
If so, then why (only) one of them is blessed and allowed to be shortened?

-j

suharik

unread,
Jun 7, 2013, 11:19:27 AM6/7/13
to golan...@googlegroups.com, m...@stretchr.com
That can be
    m1 := map...{1: "foo", 42: "bar"} // -> map[int]string, use strict types
    ss1 := [...]string{1: "foo", 42: "bar"} // already done
    ss2 := [...]...{1: "foo", 42: "bar"} // same as ss1
Sort of type inference for data.

пятница, 7 июня 2013 г., 12:36:35 UTC+4 пользователь Jan Mercl написал:

Mat Ryer

unread,
Jun 7, 2013, 11:33:55 AM6/7/13
to Jan Mercl, golang-nuts
I would actually prefer the shorthand to always be `map[string]interface{}` (a normal flexible map) and would write the longhand version if I wanted to be specific about types.

Dart has this feature, you can do this:

var map = { "something": 123, "something_else": true }

OR if you want specific types:

Map<string, num> m = { "something": 123 }

The same in Go would be:

map := {"something": 123, "something_else": true}
or
map := map[string]int{"something": 123}

I think arrays should also work:

list := [{"something":123},{"something-else":456}]

The assumed type of list would be []map[string]interface{}.

Anybody who wanted to be specific can continue to do so (this wouldn't break old code)

Mat

Martin Angers

unread,
Jun 7, 2013, 12:17:35 PM6/7/13
to golan...@googlegroups.com, Jan Mercl, m...@stretchr.com
Dart and Go have very different targets. Dart tries to replace a dynamic language (javascript), Go positions itself as a systems language (C/C++, statically typed). I definitely don't like the idea of defaulting to map[string]interface{} for the short form. It means := would sometimes infer the (static) type, other times infer what is basically a dynamic type.

I actually think the language is fine as is for the type inference and literal declarations. This is definitely not a pain point for me.

Mat Ryer

unread,
Jun 7, 2013, 1:24:08 PM6/7/13
to Martin Angers, golan...@googlegroups.com, Jan Mercl
You are right about Go and Dart having different targets, my point is only that people can cope perfectly well with having default behaviour, and being specific if they need to.

Henrik Johansson

unread,
Jun 7, 2013, 1:57:52 PM6/7/13
to Mat Ryer, Jan Mercl, golang-nuts, Martin Angers

Some default behaviours are just worse than others...

http://bugs.python.org/issue18127

Peter Bourgon

unread,
Jun 9, 2013, 3:57:05 AM6/9/13
to Mat Ryer, Jan Mercl, golang-nuts
On Fri, Jun 7, 2013 at 5:33 PM, Mat Ryer <m...@stretchr.com> wrote:
> I would actually prefer the shorthand to always be `map[string]interface{}` (a normal flexible map)

To echo other posters: it is a mistake to call this a "normal" map,
because its presence is generally a bad smell. Speaking personally,
the only time I've ever used such a thing is when I'm [un]marshaling
JSON and am being lazy -- the interface{} standing as evidence for the
latter.
Reply all
Reply to author
Forward
0 new messages