FAQ
Hi awesome list members,

Following code will break because map is nil. (
http://play.golang.org/p/qqZMrG95x7):

package main

import "fmt"

type EuclideanPoint struct {
X, Y int
}

type PointsOfInterest struct {
points map[string]EuclideanPoint
}

func (p *PointsOfInterest) Add(name string, e EuclideanPoint) {
p.points[name] = e
}

func main() {
p := PointsOfInterest{}
p.Add("home", EuclideanPoint{100, 100})
p.Add("office", EuclideanPoint{200, 200})
fmt.Println(p)
}

In my code so far, I solve this in two ways:

1. Have func NewPointsOfInterest() that returns a struct with initialized
map.
2. Have Add() func check for nil and initialize map if needed.

I don't like 1. because now everywhere has to remember to call
"constructor" and I don't like 2. because now all other methods of struct
has to perform the same check.

Is there a way to have PointsOfInterest's "zero value" be ready to use? Or
another way besides 1. and 2. I listed?

Thanks!
Boris

--

Search Discussions

  • Kyle Lemons at Dec 22, 2012 at 6:32 pm

    On Sat, Dec 22, 2012 at 1:15 PM, Boris Solovyov wrote:

    Hi awesome list members,

    Following code will break because map is nil. (
    http://play.golang.org/p/qqZMrG95x7):

    package main

    import "fmt"

    type EuclideanPoint struct {
    X, Y int
    }

    type PointsOfInterest struct {
    points map[string]EuclideanPoint
    }

    func (p *PointsOfInterest) Add(name string, e EuclideanPoint) {
    p.points[name] = e
    }

    func main() {
    p := PointsOfInterest{}
    p.Add("home", EuclideanPoint{100, 100})
    p.Add("office", EuclideanPoint{200, 200})
    fmt.Println(p)
    }

    In my code so far, I solve this in two ways:

    1. Have func NewPointsOfInterest() that returns a struct with initialized
    map.
    2. Have Add() func check for nil and initialize map if needed.

    I don't like 1. because now everywhere has to remember to call
    "constructor" and I don't like 2. because now all other methods of struct
    has to perform the same check.

    Is there a way to have PointsOfInterest's "zero value" be ready to use? Or
    another way besides 1. and 2. I listed?
    1 and 2 are both legal. You could also just allow POI to be intialized
    completely without requiring Add methods:
    http://play.golang.org/p/b_tkbf0EUQ

    --
  • Boris Solovyov at Dec 22, 2012 at 8:41 pm

    On Sat, Dec 22, 2012 at 1:32 PM, Kyle Lemons wrote:
    On Sat, Dec 22, 2012 at 1:15 PM, Boris Solovyov wrote:

    Hi awesome list members,

    Following code will break because map is nil. (
    http://play.golang.org/p/qqZMrG95x7):
    [snip]

    In my code so far, I solve this in two ways:

    1. Have func NewPointsOfInterest() that returns a struct with initialized
    map.
    2. Have Add() func check for nil and initialize map if needed.

    I don't like 1. because now everywhere has to remember to call
    "constructor" and I don't like 2. because now all other methods of struct
    has to perform the same check.

    Is there a way to have PointsOfInterest's "zero value" be ready to use?
    Or another way besides 1. and 2. I listed?
    1 and 2 are both legal. You could also just allow POI to be intialized
    completely without requiring Add methods:
    http://play.golang.org/p/b_tkbf0EUQ
    Sure. But your suggestion makes code more dependent on the internals of the
    struct that I want to abstract, so that I have to change code all over my
    repository every time I change the struct definition :-{

    Seems this is one of tradeoffs of Go... lack of magical constructor that is
    auto-executed. Well, no big deal, just wondering if there was a better way
    I did not see.

    --
  • Patrick Mylund Nielsen at Dec 22, 2012 at 8:51 pm
    A New() function is the idiomatic way if the struct needs any kind of setup
    beyond simple stuff. It is convention, but in my experience it's not
    confusing. Most structs in most libraries need some kind of setup, and thus
    have such a constructor function.

    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.

    On Sat, Dec 22, 2012 at 9:41 PM, Boris Solovyov wrote:



    On Sat, Dec 22, 2012 at 1:32 PM, Kyle Lemons wrote:

    On Sat, Dec 22, 2012 at 1:15 PM, Boris Solovyov <boris.solovyov@gmail.com
    wrote:
    Hi awesome list members,

    Following code will break because map is nil. (
    http://play.golang.org/p/qqZMrG95x7):
    [snip]

    In my code so far, I solve this in two ways:

    1. Have func NewPointsOfInterest() that returns a struct with
    initialized map.
    2. Have Add() func check for nil and initialize map if needed.

    I don't like 1. because now everywhere has to remember to call
    "constructor" and I don't like 2. because now all other methods of struct
    has to perform the same check.

    Is there a way to have PointsOfInterest's "zero value" be ready to use?
    Or another way besides 1. and 2. I listed?
    1 and 2 are both legal. You could also just allow POI to be intialized
    completely without requiring Add methods:
    http://play.golang.org/p/b_tkbf0EUQ
    Sure. But your suggestion makes code more dependent on the internals of
    the struct that I want to abstract, so that I have to change code all over
    my repository every time I change the struct definition :-{

    Seems this is one of tradeoffs of Go... lack of magical constructor that
    is auto-executed. Well, no big deal, just wondering if there was a better
    way I did not see.

    --

    --
  • Kevin Gillette at Dec 22, 2012 at 10:28 pm
    Agreed. In Go you should not go out of your way to protect users of your
    library code who can't be bothered to notice the existence of a New
    function. For example, if you run <http://play.golang.org/p/YxzyzfxNYZ>
    outside of the playground, you'll get a nil deference panic -- certainly
    the os package could have been designed to return an unexported type in
    order to protect against a naive user from using an uninitialized value,
    but that would inconvenience typical programmers and the style of adding
    runtime checks in each method is usually inefficient both for the library
    author and in the code. There are some exceptions to this (sync.Mutex,
    bytes.Buffer) where the zero value is useful, and these cases are
    documented as such.

    In general, expect users of your libraries to be competent, and you'll save
    yourself and your average users time by keeping your API simple. If you're
    worried about naive programmers: it doesn't help them if your library is
    the only one in the Go community that goes out of its way to protect them
    against their own mistakes: they will make many mistakes no matter what you
    do, and their only solution is to identify and correct their own
    shortcomings, or earnestly seek advice about how to become a better Go
    programmer.

    On Saturday, December 22, 2012 1:51:42 PM UTC-7, Patrick Mylund Nielsen
    wrote:
    A New() function is the idiomatic way if the struct needs any kind of
    setup beyond simple stuff. It is convention, but in my experience it's not
    confusing. Most structs in most libraries need some kind of setup, and thus
    have such a constructor function.

    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.


    On Sat, Dec 22, 2012 at 9:41 PM, Boris Solovyov <boris.s...@gmail.com<javascript:>
    wrote:


    On Sat, Dec 22, 2012 at 1:32 PM, Kyle Lemons <kev...@google.com<javascript:>
    wrote:
    On Sat, Dec 22, 2012 at 1:15 PM, Boris Solovyov <boris.s...@gmail.com<javascript:>
    wrote:
    Hi awesome list members,

    Following code will break because map is nil. (
    http://play.golang.org/p/qqZMrG95x7):
    [snip]

    In my code so far, I solve this in two ways:

    1. Have func NewPointsOfInterest() that returns a struct with
    initialized map.
    2. Have Add() func check for nil and initialize map if needed.

    I don't like 1. because now everywhere has to remember to call
    "constructor" and I don't like 2. because now all other methods of struct
    has to perform the same check.

    Is there a way to have PointsOfInterest's "zero value" be ready to use?
    Or another way besides 1. and 2. I listed?
    1 and 2 are both legal. You could also just allow POI to be intialized
    completely without requiring Add methods:
    http://play.golang.org/p/b_tkbf0EUQ
    Sure. But your suggestion makes code more dependent on the internals of
    the struct that I want to abstract, so that I have to change code all over
    my repository every time I change the struct definition :-{

    Seems this is one of tradeoffs of Go... lack of magical constructor that
    is auto-executed. Well, no big deal, just wondering if there was a better
    way I did not see.

    --

    --
  • Sean Russell at Dec 24, 2012 at 5:02 pm

    On Saturday, December 22, 2012 12:51:42 PM UTC-8, Patrick Mylund Nielsen wrote:

    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.
    So provide an init() function following the module init pattern, and have
    it called by make() (which would require normalizing make() so that it
    works with more than just arrays, maps, and chans). Solve two Go
    ... idiosyncrasies ... at once, without changing the behavior of any
    existing programs.

    --- SER

    --
  • Kyle Lemons at Dec 24, 2012 at 7:22 pm
    Except that has the same problem as pretty much every other bit of magic:
    make() and new() have very well-understood behavior, just as + and / do.
    If you suddenly start allowing certain types to implement arbitrary
    behavior (read: potentially unlimited side effects) you make code harder to
    read, understand, test, and debug.

    On Mon, Dec 24, 2012 at 12:02 PM, Sean Russell wrote:

    On Saturday, December 22, 2012 12:51:42 PM UTC-8, Patrick Mylund Nielsen
    wrote:
    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.
    So provide an init() function following the module init pattern, and have
    it called by make() (which would require normalizing make() so that it
    works with more than just arrays, maps, and chans). Solve two Go
    ... idiosyncrasies ... at once, without changing the behavior of any
    existing programs.

    --- SER
    --
  • Patrick Mylund Nielsen at Dec 24, 2012 at 7:34 pm
    You could say the same about function calls, then all of a sudden you're
    calling for referential transparency.

    When we call a package's New() function, we expect to get a new Foo, not
    for the package to launch the missiles. It wouldn't be unreasonable to
    think the same of new(), just as we do of a package's init() when we start
    out program. That said, I do not think that an implicit init() for a type
    is a good idea, for other reasons. Sometimes you really want just &T{}.

    On Mon, Dec 24, 2012 at 8:21 PM, Kyle Lemons wrote:

    Except that has the same problem as pretty much every other bit of magic:
    make() and new() have very well-understood behavior, just as + and / do.
    If you suddenly start allowing certain types to implement arbitrary
    behavior (read: potentially unlimited side effects) you make code harder to
    read, understand, test, and debug.

    On Mon, Dec 24, 2012 at 12:02 PM, Sean Russell wrote:

    On Saturday, December 22, 2012 12:51:42 PM UTC-8, Patrick Mylund Nielsen
    wrote:
    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.
    So provide an init() function following the module init pattern, and
    have it called by make() (which would require normalizing make() so that
    it works with more than just arrays, maps, and chans). Solve two Go
    ... idiosyncrasies ... at once, without changing the behavior of any
    existing programs.

    --- SER
    --

    --
  • Kyle Lemons at Dec 25, 2012 at 7:18 am

    On Mon, Dec 24, 2012 at 2:34 PM, Patrick Mylund Nielsen wrote:

    You could say the same about function calls, then all of a sudden you're
    calling for referential transparency.
    When debugging code, if you've already determined that function X is
    returning the wrong value but the logic itself looks right, you want to be
    able to look at the function and understand where things could be going
    wrong. In Python or C++, every method call, operator, assignment,
    construction, value leaving scope, return, index, increment, dereference,
    or comparison is a potential source of unintended side-effects (along with
    many others). In Go, on the other hand, what you see is what you get. An
    assignment is just an assignment; an addition is just an addition.
    Allocating a new object or creating a new map is just that. Calling a
    function is, and will always be, a potential error source but in Go they
    are easy to spot.

    Yes, init() is magical, and I would prefer it if importing a package
    couldn't change the behavior of your code, but I think this particular case
    is a necessity.

    When we call a package's New() function, we expect to get a new Foo, not
    for the package to launch the missiles. It wouldn't be unreasonable to
    think the same of new(), just as we do of a package's init() when we start
    out program. That said, I do not think that an implicit init() for a type
    is a good idea, for other reasons. Sometimes you really want just &T{}.

    On Mon, Dec 24, 2012 at 8:21 PM, Kyle Lemons wrote:

    Except that has the same problem as pretty much every other bit of magic:
    make() and new() have very well-understood behavior, just as + and / do.
    If you suddenly start allowing certain types to implement arbitrary
    behavior (read: potentially unlimited side effects) you make code harder to
    read, understand, test, and debug.

    On Mon, Dec 24, 2012 at 12:02 PM, Sean Russell wrote:

    On Saturday, December 22, 2012 12:51:42 PM UTC-8, Patrick Mylund Nielsen
    wrote:
    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.
    So provide an init() function following the module init pattern, and
    have it called by make() (which would require normalizing make() so
    that it works with more than just arrays, maps, and chans). Solve two Go
    ... idiosyncrasies ... at once, without changing the behavior of any
    existing programs.

    --- SER
    --

    --
  • Dan Kortschak at Dec 25, 2012 at 9:56 am
    I was thinking about this the other day. Is init really necessary? You can get an awful lot of init functionality from assigning the return value of a function to a package level variable, e.g.

    _ = func() int {
    // various side effect causing actions
    return 0
    }()

    This doesn't guarantee all variables are initialised though (I think), so that's a difference. I guess the main one. What is the order of package-level variable initialisation? It does not appear to be specified, e.g. http://play.golang.org/p/08Fa0PgVcC

    This is more ugly than an init func, but it shows that init is not really magical (except for the timing guarantee provided by an init).

    Dan
    On 25/12/2012, at 5:48 PM, "Kyle Lemons" wrote:

    Yes, init() is magical, and I would prefer it if importing a package couldn't change the behavior of your code, but I think this particular case is a necessity.
    --
  • Chris dollin at Dec 25, 2012 at 10:07 am

    On 25 December 2012 09:56, Dan Kortschak wrote:
    What is the order of package-level variable initialisation? It does not appear to be specified, e.g. http://play.golang.org/p/08Fa0PgVcC
    http://golang.org/ref/spec#Program_execution

    ...

    Within a package, package-level variables are initialized, and
    constant values are determined, in data-dependent order: if the
    initializer of A depends on the value of B, A will be set after B. It
    is an error if such dependencies form a cycle. Dependency analysis is
    done lexically: A depends on B if the value of A contains a mention of
    B, contains a value whose initializer mentions B, or mentions a
    function that mentions B, recursively. If two items are not
    interdependent, they will be initialized in the order they appear in
    the source. Since the dependency analysis is done per package, it can
    produce unspecified results if A's initializer calls a function
    defined in another package that refers to B.

    (etc)

    Chris

    --
    Chris "allusive" Dollin

    --
  • Dan Kortschak at Dec 25, 2012 at 10:30 am
    Thanks, I actually read that and yet somehow completely managed to miss everything. Too much Christmas.

    So you can potentially mimic an init function perfectly with that technique.
    On 25/12/2012, at 8:37 PM, "chris dollin" wrote:
    On 25 December 2012 09:56, Dan Kortschak wrote:

    What is the order of package-level variable initialisation? It does not appear to be specified, e.g. http://play.golang.org/p/08Fa0PgVcC
    http://golang.org/ref/spec#Program_execution

    ...

    Within a package, package-level variables are initialized, and
    constant values are determined, in data-dependent order: if the
    initializer of A depends on the value of B, A will be set after B. It
    is an error if such dependencies form a cycle. Dependency analysis is
    done lexically: A depends on B if the value of A contains a mention of
    B, contains a value whose initializer mentions B, or mentions a
    function that mentions B, recursively. If two items are not
    interdependent, they will be initialized in the order they appear in
    the source. Since the dependency analysis is done per package, it can
    produce unspecified results if A's initializer calls a function
    defined in another package that refers to B.

    (etc)

    Chris

    --
    Chris "allusive" Dollin
    --
  • Kevin Gillette at Dec 25, 2012 at 7:07 pm
    Why would constants need to be determined at runtime? The whole point is
    that they're constants, which cannot be composed of non-constant
    expressions, and thus can packed into the text segment during compilation,
    or set to be all bulk loaded into memory before any package initialization
    occurs.
    On Tuesday, December 25, 2012 3:07:40 AM UTC-7, chris dollin wrote:
    On 25 December 2012 09:56, Dan Kortschak wrote:

    What is the order of package-level variable initialisation? It does not
    appear to be specified, e.g. http://play.golang.org/p/08Fa0PgVcC

    http://golang.org/ref/spec#Program_execution

    ...

    Within a package, package-level variables are initialized, and
    constant values are determined, in data-dependent order: if the
    initializer of A depends on the value of B, A will be set after B. It
    is an error if such dependencies form a cycle. Dependency analysis is
    done lexically: A depends on B if the value of A contains a mention of
    B, contains a value whose initializer mentions B, or mentions a
    function that mentions B, recursively. If two items are not
    interdependent, they will be initialized in the order they appear in
    the source. Since the dependency analysis is done per package, it can
    produce unspecified results if A's initializer calls a function
    defined in another package that refers to B.

    (etc)

    Chris

    --
    Chris "allusive" Dollin
    --
  • Kevin Gillette at Dec 25, 2012 at 1:07 am
    I don't think "lack of magic" in a language is idiosyncratic. The language
    does not have any special methods for special behavior. Adding
    special-cased support for an init() method _would_ create an idiosyncrasy.
    On Monday, December 24, 2012 10:02:44 AM UTC-7, Sean Russell wrote:

    On Saturday, December 22, 2012 12:51:42 PM UTC-8, Patrick Mylund Nielsen
    wrote:
    It might have been convenient if types had special init() methods, and
    new() caused them to be invoked, but it would cause some confusion with
    composite literals/cases where you actually want the fields to be
    uninitialized.
    So provide an init() function following the module init pattern, and have
    it called by make() (which would require normalizing make() so that it
    works with more than just arrays, maps, and chans). Solve two Go
    ... idiosyncrasies ... at once, without changing the behavior of any
    existing programs.

    --- SER
    --
  • Sean Russell at Dec 25, 2012 at 1:29 am

    On Monday, December 24, 2012 5:07:34 PM UTC-8, Kevin Gillette wrote:
    I don't think "lack of magic" in a language is idiosyncratic. The language
    does not have any special methods for special behavior. Adding
    special-cased support for an init() method _would_ create an idiosyncrasy.
    You already have the magic. Modules in Go already have an init() function.
    It's opaque, black box magic that "launches missiles" when you include and
    use a package, if it is defined.

    Go has a lot of things that are idiosyncratic in that they're not intuitive
    (extrapolate-able based on partial knowledge of the language rules) across
    the language: this is one of them. In many cases, there are good, well
    thought-out reasons for the inconsistency. Maybe there's a good reason in
    this case, too. "Protecting developers from themselves because developers
    are stupid" is not one of them.

    --- SER

    --
  • Mike Rosset at Dec 25, 2012 at 5:25 am
    I'm not sure I would go so far as to say func init is magic . Its
    really only there to setup internal package state. Done right it
    should not really bite anyone in the ass. GOROOT/src/pkg/fmt/format.go
    is a good example of using init to initialize some internal maps, all
    of which is hidden from the user of the package.
    On Mon, Dec 24, 2012 at 5:29 PM, Sean Russell wrote:
    On Monday, December 24, 2012 5:07:34 PM UTC-8, Kevin Gillette wrote:

    I don't think "lack of magic" in a language is idiosyncratic. The language
    does not have any special methods for special behavior. Adding special-cased
    support for an init() method _would_ create an idiosyncrasy.

    You already have the magic. Modules in Go already have an init() function.
    It's opaque, black box magic that "launches missiles" when you include and
    use a package, if it is defined.

    Go has a lot of things that are idiosyncratic in that they're not intuitive
    (extrapolate-able based on partial knowledge of the language rules) across
    the language: this is one of them. In many cases, there are good, well
    thought-out reasons for the inconsistency. Maybe there's a good reason in
    this case, too. "Protecting developers from themselves because developers
    are stupid" is not one of them.

    --- SER

    --
    --

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedDec 22, '12 at 6:15p
activeDec 25, '12 at 7:07p
posts16
users8
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase