Enforce immutability through static analysis

178 views
Skip to first unread message

advand...@gmail.com

unread,
Nov 21, 2019, 11:16:37 AM11/21/19
to golang-nuts
Dear Gophers!

I was wonder if it possible to force immutability on the method receiver? I know Go doesn't support immutable types and that it is possible to pass the receiver by value but if the receiver struct has a field with a pointer type the method may still manipulate it:

type Counter struct {
 n
*int
}

func
(c Counter) Render() string {
 
*c.n += 1
 
return strconv.Itoa(*c.n)
}

I would like to force (or hint) the the user in writing interface{ Render() string } implementations that don't manipulate the method receiver. So that they can be considered 'pure' in the functional sense of the word and can be called repeatedly without side effects. I would like the user to be able to define implementations of interface{ Render() string }such that I can safely call the method and use the returned string to write a http.Reponse without it changing between requests. 

I control the way in which Render is called and I am open to crazy answers such as:

- Maybe it is possible to use reflect to "switch" out the value receiver for a temporary value which is discarded after every call?
- Maybe i can use static code analysis to warn the user? How feasible is it to prevent all cases of this happening with just static code analysis? can this be done at runtime?
- I could instead ask the user to provide a factory function that init new Counters but maybe very inefficient if the structs are very large (or have many nested structs)?

Or maybe there is some possibility that I'm missing?

Cheers,

Ad


Robert Engels

unread,
Nov 21, 2019, 11:49:40 AM11/21/19
to advand...@gmail.com, golang-nuts
They can't unless the instance field is exported. Just hide it via encapsulation with accessors.

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/7ee35405-fef4-415b-ae5d-95322b4065aa%40googlegroups.com.



burak serdar

unread,
Nov 21, 2019, 11:54:34 AM11/21/19
to Robert Engels, advand...@gmail.com, golang-nuts
On Thu, Nov 21, 2019 at 9:49 AM Robert Engels <ren...@ix.netcom.com> wrote:
>
> They can't unless the instance field is exported. Just hide it via encapsulation with accessors.

Can't do that with a receiver. All methods of a type are in the same
package as the type, so all fields are visible to the receiver.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/1622995561.1365.1574354931169%40wamui-scooby.atl.sa.earthlink.net.

Robert Engels

unread,
Nov 21, 2019, 11:58:19 AM11/21/19
to burak serdar, advand...@gmail.com, golang-nuts

Correct, but if the receiver method is mutating it, then it is not an immutable object.

Robert Engels

unread,
Nov 21, 2019, 12:05:44 PM11/21/19
to Robert Engels, burak serdar, advand...@gmail.com, golang-nuts
To clarify - the author of the package enforces immutability. With Go’s design this can be a simple comment on the field. The package shouldn’t be that large where this doesn’t work.

> On Nov 21, 2019, at 10:58 AM, Robert Engels <ren...@ix.netcom.com> wrote:
>
> 
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/2080138990.1391.1574355466613%40wamui-scooby.atl.sa.earthlink.net.

burak serdar

unread,
Nov 21, 2019, 12:10:34 PM11/21/19
to Robert Engels, advand...@gmail.com, golang-nuts
On Thu, Nov 21, 2019 at 10:05 AM Robert Engels <ren...@ix.netcom.com> wrote:
>
> To clarify - the author of the package enforces immutability. With Go’s design this can be a simple comment on the field. The package shouldn’t be that large where this doesn’t work.

The original problem remains: there is no way to enforce an immutable receiver.

Robert Engels

unread,
Nov 21, 2019, 12:25:36 PM11/21/19
to burak serdar, advand...@gmail.com, golang-nuts
I don't think we are talking about the same thing. You can certainly code an immutable object - just don't export any methods that mutate the object, nor export ANY fields.

burak serdar

unread,
Nov 21, 2019, 12:34:49 PM11/21/19
to Robert Engels, advand...@gmail.com, golang-nuts
On Thu, Nov 21, 2019 at 10:25 AM Robert Engels <ren...@ix.netcom.com> wrote:
>
> I don't think we are talking about the same thing. You can certainly code an immutable object - just don't export any methods that mutate the object, nor export ANY fields.

Correct, we're talking about different things. The question is not
whether you can write an immutable object (yes you can), it is whether
there is a way to enforce the immutability of the receiver of a
method.

If the method is exported and if the receiver contains pointers, there
can be no guarantee that the method will not modify values reachable
from the copy of the receiver.

Robert Engels

unread,
Nov 21, 2019, 12:39:20 PM11/21/19
to burak serdar, advand...@gmail.com, golang-nuts


I'm sorry, but isn't the way you enforce immutability is that you don't code mutating exported methods? The author controls the package code. Immutability is usually only a concern outside the package, and that is clearly supported.

burak serdar

unread,
Nov 21, 2019, 1:01:09 PM11/21/19
to Robert Engels, advand...@gmail.com, golang-nuts
On Thu, Nov 21, 2019 at 10:39 AM Robert Engels <ren...@ix.netcom.com> wrote:
>
>
>
> I'm sorry, but isn't the way you enforce immutability is that you don't code mutating exported methods? The author controls the package code. Immutability is usually only a concern outside the package, and that is clearly supported.

The way I read the original post what is being asked is, is it
possible to have the Go-equivalent of the following C++ code:

class X{
public:
virtual int f() const =0;

Robert Engels

unread,
Nov 21, 2019, 1:20:07 PM11/21/19
to burak serdar, advand...@gmail.com, golang-nuts

Yes, I agree. But in some sense, a developer needed to add the 'const', i.e. decide that the method was immutable as part of the design (and since Go doesn't have inheritance it is less important). Go doesn't support this through syntax/compiler it supports it through design philosophy - if the packages are well done, and right-sized, the "immutable design" is easily "enforced & validated". I also think this can lead to easier to understood designs - rather than just slapping a bunch of const declarations.

Java is in a similar boat (a bit more difficult since it has inheritance), and it is not a problem with proper design. There are TONS of "immutable" Java libraries.

Just my opinion of course :)
>To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAMV2RqqYdfYdEVbGrOtNiSa45Vgn6ZCibP3Gqrn%2BRe-%2BmFJVBg%40mail.gmail.com.

Ad van der Veer

unread,
Nov 22, 2019, 3:06:36 AM11/22/19
to Robert Engels, burak serdar, golang-nuts
Thank you for the responses! I understand that i'm trying to do something that the language doesn't really support, and that is fine; i'm a huge advocate of Go so i'm not trying to point out language weaknesses.

In my i'm dictating the interface{} that the user should implement to allow its "component" (the struct implementing the interface) to be rendered so it's hard to enforce immutability with setters and getters since the properties are in control of the user so i can't dictate an interface for that upfront. 

Robert Johnstone

unread,
Nov 22, 2019, 9:40:22 AM11/22/19
to golang-nuts
This comment is a little unfair.  There was at one time efforts to allow const as part of the type system.  I believe that the specific motivation was to allow []const byte to ease conversions are eliminate conversions from strings.  The current duplication of bytes and strings packages is a bit of a smell.  

If you had instead written an example for an interface, the request would not seem so ridiculous:

  interface X {
    const f() int
  }

(Not advocating a change to the language, just pointing out that the question should not be mocked)
> >> >> >>> To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.
> >> >> >>> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/7ee35405-fef4-415b-ae5d-95322b4065aa%40googlegroups.com.
> >> >> >>>
> >> >> >>>
> >> >> >>>
> >> >> >>>
> >> >> >>> --
> >> >> >>> 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 golan...@googlegroups.com.
> >> >> >>> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/1622995561.1365.1574354931169%40wamui-scooby.atl.sa.earthlink.net.
> >> >> >
> >> >> > --
> >> >> > 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 golan...@googlegroups.com.

Robert Engels

unread,
Nov 22, 2019, 10:23:09 AM11/22/19
to Robert Johnstone, golang-nuts

I don't think anyone was mocking the question or thought it was ridiculous.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/722064fa-60a1-4fac-a239-52e3b915de63%40googlegroups.com.



Paul Jolly

unread,
Nov 22, 2019, 10:41:20 AM11/22/19
to advand...@gmail.com, golang-nuts
May or may not be of interest, but I experimented with an alternative
approach in https://github.com/myitcv/x/blob/master/immutable/_doc/immutableGen.md

Although this is where the term "immutable" gets massively overloaded,
because the result of immutableGen is (as I understand it) persistent
data structures. My use of the term "immutable" is more a nod to
https://github.com/immutable-js/immutable-js which inspired the
pattern.

That caveat aside.

It's a code generation approach whereby you declare immutable templates, e.g.

type _Imm_Banana struct {
Name string
age int
}

You then get code generated get/set methods that respect the
exported-ness of the template fields.

immutableVet can then be used to verify correct use of struct fields
within a package, i.e. that you don't have any non-generated code that
subverts the get/set methods.

You can also have "immutable" maps and slices.
> --
> 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.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/7ee35405-fef4-415b-ae5d-95322b4065aa%40googlegroups.com.

Jake Montgomery

unread,
Nov 22, 2019, 11:51:36 AM11/22/19
to golang-nuts
I'm afraid your question is a bit confusing, which may account for some of the responses you have received so far. Here is what I think you are asking for. You want to define an interface which your package will use, and other packages will implement. You want to put restrictions on what these implementation can do within the methods that satisfy the interface. In this case it is immutability. Is that correct?

If so, then I believe the simple answer is no. If packages over which you have no control implement your interface you can not enforce what those methods do. Your only real option is to clearly document in your interface definition what you expect.

But that's not all that problematic. This is really true for any aspect of the functionality implemented by an interface. Simply matching the method signatures is enough for the compiler to allow a type to implement the interface. But in almost every case that is not actually enough to satisfy the intent of the interface. The coder must carefully take into account the required documented behavior of the interface as well. If a coder chooses to implement an interface in a way that violates the documented requirements then they are a bad coder, and should expect bad results when they try to use that interface. Go coders are used to reading and following the interface requirements as documented.
Reply all
Reply to author
Forward
0 new messages