Maybe i'm wrong, but i really feel that keeping go minimal is enabling lots of experiments with it, as well as providing very powerful tools around the language. I'm not sure what design "school" it is following (maybe unix single purpose command, but maybe there's something more relevant), but authors of the language really made an interesting point by sticking to their initial feeling, and delivering.
Although i wouldn't use go for "high level" development (that is, end-user apps or web services), it has really proved to be a great tool for middleware / technical layers.
Even for templating it doesn't have to be a bad choice. I use it for templating and while it's certainly not the most powerful tool out there, it allows to quickly create site engines that perform very well (in case existing tools don't fit the use case).
Not a fan of the built-in template syntax but there are luckily good (and faster) alternatives.
User-facing applications or web services often deal with business logic. That is, logic that models real-life processes and entities, such as "Customer" or "Purchase". In order to model those kind of processes you often need powerful modelling abilities (such as subclassing, or generic programming), which go really lacks. I don't have a proof of that just my personal experience, but i'd say Object Oriented Programming is the de-facto standard in "enterprise" software for that reason.
The reason go fits the "technical layer" best, is that technical layer deals with things closer to the machine, so the number of different types you need to manipulate is much lower, and is for the most part of the language (it doesn't go further than numbers and strings).
All of this is just my personal opinion of course.
EDIT:
i know composition (which go provides) and subclassing provide the same possibilities in term of modeling, in theory. However in practice, not beeing able to say things like "An Electric Car, is a kind of Car, which is a kind of Vehicle" may make things cumbersome. So is not being able to simply create a function such as "sortByMileage" that applies to all kinds of Vehicle (and just that), but remain type safe.
To sum-up : relying on interfaces and composition only is IMHO not as powerful as having interface, composition, inheritance and generics (which most "enterprise" language have).
Note that you can certainly express "An Electric Car, is a kind of Car, which is a kind of Vehicle" in Go by using interfaces - albeit such kinds of relations are rarely needed in business logic in my experience.
It's an optimisation against the OOP vs relational mismatch, but the fact that it's here proves that OOP subclassing is being used quite often (otherwise there wouldn't be any need to optimise for it, at least not by adding core features to pg).
"You can indeed, but it makes you duplicate every implementation with its associated interface."
Only if A: composition can't do the job somehow and B: you can't use perfectly normal factoring tools to do the work.
While I will tell you before anybody else complains that there is a non-zero percentage of the time that will happen, I will also tell you the average person who has never tried Go yet complains about its lack of generics anyhow will overestimate the amount of time that happens by a good order of magnitude or two.
One remark (that may seem like a nitpick, but is in fact crucial) : composition doesn't say it "is a kind of", but rather "it has".
It's the two most fundamental concept of any language : to be vs to have. Now it may or may not be just a philosophical difference and bring no practical consequences to a computer program, but at the minimum one can say it expresses two different concepts.
Actually, not having inheritance is what I love the most about go. When I write ruby code, I keep wondering "where does this method come from?" and have to grep my codebase.
"Composition over inheritance" is not a new trend, that was actually mentioned in first chapter of Gang of four's Design Patterns in 1995 [1]. In that regard, I feel like we mostly failed to ever use OOP properly (although, "fail" is probably too strong a word).
I'm actually annoyed that embedded types exist in go because it re-introduces the same problem (but I have an easy fix for that: I don't use them).
But really, what you're describing is a data structure problem. This can be solved without OOP, and structs are especially good at it.
Pattern matching interfaces also makes finding exactly what underlying object is being used in a given context difficult as well. With explicitly declared interfaces, you can easily find all implementations. With pattern matching it's a bit more complicated.
Yeah indeed, there's that too, although I'm glad we can't define methods on interfaces (using the interface as method receiver), so it quite reduces the complexity to just having to figure out the actual type, most often found just by inspecting the caller.
> When I write ruby code, I keep wondering "where does this method come from?" and have to grep my codebase.
Sounds like a shortcoming of your tools. I spend a lot of time in Visual Studio working on native C++ and even there I can ctrl-click on something and jump to the declaration or definition. I bet there's something even better for Ruby.
Yeah probably, I don't use any IDE feature for ruby code. It's still a problem that all methods available in current scope are not immediately obvious, though. And if you try to inspect available methods on an object in a REPL console using `#methods`, there usually are hundredth of them, especially when using rails (although to be fair, it's not all due to inheritance, there are also a lot coming for mixins). I suppose ruby IDEs have the same problem.
When I write go code, I have a lot of static analysis tools, but I'm still glad it's easy for me to know what I can call without stopping for inspection.
Unfortunately jumping to definitions in Ruby is often hit-or-miss since the language is very dynamic and idiomatic Ruby code often uses a fair amount of metaprogramming.
I still think it's why some of the world's most complex software such as our operating systems, proxies, load balancers and web servers such as HAProxy and nginx are written in C -- raw speed aside. C itself is also a minimal language.
I am not sure if it is a design school, but rather I think when you afford the ability to truly have mastery over your tool, then the humans using them can take them and create something you could have never envisioned yourself.
The popular approach seems to be these days to give them every single tool (and toolbelt), and they get lost in the chaos. (e.g "Is C++'s problem really that it needs more features?") I spent my first part of my career as a developer writing Java. I still don't feel like I know half of the language. Maybe this is because I have a feeble mind and can't grok these higher level concepts, or maybe it's because I didn't have time to learn 5 different programming paradigms under the same roof when I had work to do, I don't know the real answer. However, I have spent just under 2 writing full time Go and I feel like I have mastery over the entire language (I credit this to it's very digestible language specification).
It's the simplicity that makes it so beautiful and able to create the powerful tools. To draw a parallel in literature (a quip I read in a book recently), consider Shakespeare who wrote his masterpieces in blank verse. There were many other advanced forms of poetry, prose, and literary forms developed by then, but he chose this. Blank verse is not inherently complex, in fact it was used because of it's simplicity and ease of remembrance. Yet it was because of this simplicity and minimal structure that he was able to create some of the most beautiful artistic expressions of the english language.
My instinctive response is "but it's garbage collected, so what use does that have on embedded?". Most of the stuff I'm doing is fairly soft real time though.
Am I being too closed minded and a garbage collector that ran quickly enough would still allow for a useful design space?
By this definition embedded development will go extinct very soon.
And maybe it should. I think as far as software is concerned embedded development is not all that special anymore. Everything is running advanced ARM SoCs that have the same power you'd have on a workstation ~15 years ago.
It's still meaningful if you're working on electronics though, you generally have wildly different constraints regarding ECM, power draw, heat dissipation, environmental constraints etc...
No, the majority of embedded systems are still on Cortex-M level systems, not "A".
Despite what the maker-movement and IoT-hype would have you believe, most embedded systems are not running Linux.
Bear in mind that the vast majority of processors sold these days are micro-controllers and go into things like electricity meters, light switches, alarm clocks, etc.... mundane stuff.
Sure but it's definitely moving that way in my experience. As the price difference becomes more and more negligible people prefer to stick more fully featured chips in products where it wouldn't have made sense 10 years ago.
There's benefit for companies to do that too, developers who can work efficiently on an MMU-less bare metal/small RTOS system tend to be experienced and cost a lot. Now if you run Linux (or maybe even android these days) on your embedded product you can basically have any half competent junior coder develop on it without problem.
At the rate this is going I would only be half surprised if my electricity meter ran Linux in a decade, especially since the trend is into making "smarter" ones.
Also while Cortex-M are definitely more microcontroller-ish than the A versions they're still pretty impressive beasts compared to the microcontrollers of yore. They can run at frequencies on the order of hundreds of MHz, often come with advanced IPs like USB, ethernet, DMA and more. Maybe we should have an intermediate category for "millicontrollers".
(As someone who has been in smart-metering for the past 6-ish years, and been s/w architect for both elec and gas smart meters, the situation is that the residential meters (i.e. high unit count) demands the cheapest and smallest processor that the s/w will run on.
Industrial meters OTOH, are relatively high-cost and can justify a Linux-based device (and indeed there are examples out there that have gone this way). Tho due to commonality with the residential meters, it is more efficient for them to be based on the same hardware generally.
All of them produced workable graphical workstations that were used for a couple of years until the projects were shutdown for political/monetary reasons.
Oberon GUI is the genesis how Plan 9/Inferno GUIs work, specially the ACME text editor.
For something with more constraints, Real-Time Java implementations are powering battleship weapons tracking systems, here two examples
Having said this, of course there are environments where not having a GC at all is a better approach for the use case being solved.
What many in the anti-GC crowd don't realise it that these GC enabled systems languages also allow to switch off the GC on critical code sections, or make use of manual memory management in unsafe code.
So it it not necessary to throw away the productivity to get the full benefit of performance, if the language offers the necessary knobs.
What exactly do you mean? Go doesn't allow you to manage memory. At most you can give some hints to the compiler but they may or may not work depending on the compiler/version.
>> 3.1 - if there is an OS underneath call its allocation/release APIs directly,
3.2 - running on bare metal, implement a manual allocator with a mix of package unsafe and Assembly for CPU register access
That seems to require some patches to the runtime and compiler not to mention that you start to depart from what you might remember as being Go language/development. You create more problems to fix the GC problem.
I believe a real solution would be an opt-in subset of Go using Rust-like memory model. Swift(Apple) seems to work on a such solution.
Sure it does, you just have to do it like in the good old days.
The easy way is to make use of object pooling via sync/pool.
The hard way is to declare a static array allocated as global variable in a package for the set of data that one needs to manage and make use of pointers to free slots.
What patches to runtime and compiler?
You are mixing up programming language and implementation.
There are no pure ANSI C compilers for bare metal programming, 100% of them require language extensions.
My guess is it can be managed by something similar to the concept of critical sections. That is, GC is disabled in the critical sections and is only enabled explicitly by the programmer. That's like how the pauses caused by hardware interrupts are prevented in kernel/driver programming. After all, you won't like heap allocation/deallocation in the hot-path no matter what language is used, be it GCed or not.
The problem with GC in hard-real-time systems is not speed... real-time != fast.
real-time == deterministic, and this is what GC is not. At all times you must know what code is able to execute in order to meet worst-case deadlines.
The easiest way of managing this is to disallow any non-deterministic processes in the real-time levels of your application (this means dynamic memory allocation and by inference also GC, but it also applies to anything else).
Also bear in mind that most hard-real-time systems are also safety-related and need high-reliability. systems are expected to stay running indefinitely (or at least for years) without a reset on systems with Kilobytes of RAM.
For these types of systems you work out your worst-case load and statically allocate everything. This makes it much easier to reason about.
The era that that particular one was written in, java was still accepted in RT systems.
These days, that is not the case (I've worked on them myself). They are typically written in C/C++/ADA.
Bear in mind that there are typically many levels to something like an ESM system, the lower levels would always be in something like C/C++/ADA (or possibly FPGA) but if Java does come into it, it would be for the UI but even that is more likely C# these days. This is not the RT level code.
Depends on your embedded system and your use case.
If your embedded system is a Raspberry Pi 3 controlling a few leds or talking with something over UART or whatever, then it won't matter a whole lot whether you have a GC or not.
On the other hand, if your embedded system is a 50Mhz 8-bit microcontroller with a whooping 8KB of "RAM" (and 16KB of program memory) doing ~20k sensor reads per second, then it WILL matter.
They seem to be targeting armv7 and their reference device (Wanboard Quad) has a 1GHz clock and 2GB of ram, which is a monster compared to most embedded systems I've worked on.
That gives them plenty of cycles and memory to waste doing garbage collection, a luxury SOME of us can't afford.
Aside from what everyone else said, you should keep in mind that garbage collection != Java memory model.
Go has structs and defaults to avoiding boxed values. It also has escape analysis and tries to do stack allocation before putting objects on the heap.
Compared to Java, it is a lot easier to write your code in a way that minimises or even avoids generating garbage that has to be collected in the first place.
53 comments
[ 3.0 ms ] story [ 53.0 ms ] threadAlthough i wouldn't use go for "high level" development (that is, end-user apps or web services), it has really proved to be a great tool for middleware / technical layers.
Not a fan of the built-in template syntax but there are luckily good (and faster) alternatives.
The reason go fits the "technical layer" best, is that technical layer deals with things closer to the machine, so the number of different types you need to manipulate is much lower, and is for the most part of the language (it doesn't go further than numbers and strings).
All of this is just my personal opinion of course.
EDIT: i know composition (which go provides) and subclassing provide the same possibilities in term of modeling, in theory. However in practice, not beeing able to say things like "An Electric Car, is a kind of Car, which is a kind of Vehicle" may make things cumbersome. So is not being able to simply create a function such as "sortByMileage" that applies to all kinds of Vehicle (and just that), but remain type safe.
To sum-up : relying on interfaces and composition only is IMHO not as powerful as having interface, composition, inheritance and generics (which most "enterprise" language have).
As for this kind of relationship, i'd say it's fairly widespread. One proof is that even relationnal DB such as postgresql added table inheritance.
Only if A: composition can't do the job somehow and B: you can't use perfectly normal factoring tools to do the work.
While I will tell you before anybody else complains that there is a non-zero percentage of the time that will happen, I will also tell you the average person who has never tried Go yet complains about its lack of generics anyhow will overestimate the amount of time that happens by a good order of magnitude or two.
It's the two most fundamental concept of any language : to be vs to have. Now it may or may not be just a philosophical difference and bring no practical consequences to a computer program, but at the minimum one can say it expresses two different concepts.
But it perfectly allows you to model complex real-life processes, and in a way much better than traditional class-based OOP approach.
"Composition over inheritance" is not a new trend, that was actually mentioned in first chapter of Gang of four's Design Patterns in 1995 [1]. In that regard, I feel like we mostly failed to ever use OOP properly (although, "fail" is probably too strong a word).
I'm actually annoyed that embedded types exist in go because it re-introduces the same problem (but I have an easy fix for that: I don't use them).
But really, what you're describing is a data structure problem. This can be solved without OOP, and structs are especially good at it.
[1] https://en.wikipedia.org/wiki/Design_Patterns#Introduction.2...
Sounds like a shortcoming of your tools. I spend a lot of time in Visual Studio working on native C++ and even there I can ctrl-click on something and jump to the declaration or definition. I bet there's something even better for Ruby.
When I write go code, I have a lot of static analysis tools, but I'm still glad it's easy for me to know what I can call without stopping for inspection.
And then you have a hybrid car with a drilling attachment.
I am not sure if it is a design school, but rather I think when you afford the ability to truly have mastery over your tool, then the humans using them can take them and create something you could have never envisioned yourself.
The popular approach seems to be these days to give them every single tool (and toolbelt), and they get lost in the chaos. (e.g "Is C++'s problem really that it needs more features?") I spent my first part of my career as a developer writing Java. I still don't feel like I know half of the language. Maybe this is because I have a feeble mind and can't grok these higher level concepts, or maybe it's because I didn't have time to learn 5 different programming paradigms under the same roof when I had work to do, I don't know the real answer. However, I have spent just under 2 writing full time Go and I feel like I have mastery over the entire language (I credit this to it's very digestible language specification).
It's the simplicity that makes it so beautiful and able to create the powerful tools. To draw a parallel in literature (a quip I read in a book recently), consider Shakespeare who wrote his masterpieces in blank verse. There were many other advanced forms of poetry, prose, and literary forms developed by then, but he chose this. Blank verse is not inherently complex, in fact it was used because of it's simplicity and ease of remembrance. Yet it was because of this simplicity and minimal structure that he was able to create some of the most beautiful artistic expressions of the english language.
Just like the most complex Web applications are written in JavaScript.
There is definitely truth to that. Go has taken over the Cloud, so there might even be parallel there as well.
Am I being too closed minded and a garbage collector that ran quickly enough would still allow for a useful design space?
https://twitter.com/brianhatfield/status/634166123605331968
Nowadays anything from 16kB to 2GB of RAM gets called "embedded". Storage is the same but x10.
I would be surprised if there's nothing to use Go for in all that design space, but generalizations are probably plain impossible.
Not always used tho.
And maybe it should. I think as far as software is concerned embedded development is not all that special anymore. Everything is running advanced ARM SoCs that have the same power you'd have on a workstation ~15 years ago.
It's still meaningful if you're working on electronics though, you generally have wildly different constraints regarding ECM, power draw, heat dissipation, environmental constraints etc...
Despite what the maker-movement and IoT-hype would have you believe, most embedded systems are not running Linux.
Bear in mind that the vast majority of processors sold these days are micro-controllers and go into things like electricity meters, light switches, alarm clocks, etc.... mundane stuff.
MicroEJ is operating since 2004.
If they had practically zero customers, they would have closed doors by now.
There's benefit for companies to do that too, developers who can work efficiently on an MMU-less bare metal/small RTOS system tend to be experienced and cost a lot. Now if you run Linux (or maybe even android these days) on your embedded product you can basically have any half competent junior coder develop on it without problem.
At the rate this is going I would only be half surprised if my electricity meter ran Linux in a decade, especially since the trend is into making "smarter" ones.
Also while Cortex-M are definitely more microcontroller-ish than the A versions they're still pretty impressive beasts compared to the microcontrollers of yore. They can run at frequencies on the order of hundreds of MHz, often come with advanced IPs like USB, ethernet, DMA and more. Maybe we should have an intermediate category for "millicontrollers".
Have a look at Project Oberon, Mesa/Cedar at Xerox PARC, Modula-3 at DEC/Olivetti, Singularity/Midori at Microsoft
https://en.wikipedia.org/wiki/Oberon_(operating_system)
http://www.ocp.inf.ethz.ch/wiki/Documentation/Front
https://en.wikipedia.org/wiki/SPIN_(operating_system)
https://archive.org/details/bitsavers_xeroxparcteCedarProgra...
http://joeduffyblog.com/2015/11/03/blogging-about-midori/
https://www.infoq.com/presentations/csharp-systems-programmi...
All of them produced workable graphical workstations that were used for a couple of years until the projects were shutdown for political/monetary reasons.
Oberon GUI is the genesis how Plan 9/Inferno GUIs work, specially the ACME text editor.
For something with more constraints, Real-Time Java implementations are powering battleship weapons tracking systems, here two examples
http://www.military.com/equipment/aegis-weapon-system
http://www.marketwired.com/press-release/ibm-raytheon-delive...
Having said this, of course there are environments where not having a GC at all is a better approach for the use case being solved.
What many in the anti-GC crowd don't realise it that these GC enabled systems languages also allow to switch off the GC on critical code sections, or make use of manual memory management in unsafe code.
So it it not necessary to throw away the productivity to get the full benefit of performance, if the language offers the necessary knobs.
1 - Use a memory profiler to ensure the memory usage is kept on constant level;
2 - Reduce GC pressure by manually managing memory at Go level;
3 - If still not enough,
3.1 - if there is an OS underneath call its allocation/release APIs directly,
3.2 - running on bare metal, implement a manual allocator with a mix of package unsafe and Assembly for CPU register access
It is not the same as using untraced references in Modula-3, but it does the job.
Also I should note that even ANSI C requires use of Assembly in libc to implement malloc()/free() on bare metal.
What exactly do you mean? Go doesn't allow you to manage memory. At most you can give some hints to the compiler but they may or may not work depending on the compiler/version.
>> 3.1 - if there is an OS underneath call its allocation/release APIs directly, 3.2 - running on bare metal, implement a manual allocator with a mix of package unsafe and Assembly for CPU register access
That seems to require some patches to the runtime and compiler not to mention that you start to depart from what you might remember as being Go language/development. You create more problems to fix the GC problem. I believe a real solution would be an opt-in subset of Go using Rust-like memory model. Swift(Apple) seems to work on a such solution.
The easy way is to make use of object pooling via sync/pool.
The hard way is to declare a static array allocated as global variable in a package for the set of data that one needs to manage and make use of pointers to free slots.
What patches to runtime and compiler?
You are mixing up programming language and implementation.
There are no pure ANSI C compilers for bare metal programming, 100% of them require language extensions.
real-time == deterministic, and this is what GC is not. At all times you must know what code is able to execute in order to meet worst-case deadlines.
The easiest way of managing this is to disallow any non-deterministic processes in the real-time levels of your application (this means dynamic memory allocation and by inference also GC, but it also applies to anything else).
Also bear in mind that most hard-real-time systems are also safety-related and need high-reliability. systems are expected to stay running indefinitely (or at least for years) without a reset on systems with Kilobytes of RAM.
For these types of systems you work out your worst-case load and statically allocate everything. This makes it much easier to reason about.
Could you please tell some of them? thanks
- Engine management systems (e.g. timing of firing in an IC engine)
- Missile detection systems (e.g. need to react to a detection within time to activate countermeasures).
- Medical systems (e.g. timing of valve closing/opening is critical to dosage).
- Phone protocol stacks (e.g. transmitting during allocated time-slots).
- Metering systems (e.g. must read measurement devices at correct time to ensure accuracy).
- Audio systems (e.g. avoiding glitches requires deterministic processing)
...and lots, lots more.
In general, things that interact with the real physical world require deterministic control.
Nowadays partly written in Real Time Java, can gladly provide examples from military web sites.
Bear in mind that there are typically many levels to something like an ESM system, the lower levels would always be in something like C/C++/ADA (or possibly FPGA) but if Java does come into it, it would be for the UI but even that is more likely C# these days. This is not the RT level code.
If your embedded system is a Raspberry Pi 3 controlling a few leds or talking with something over UART or whatever, then it won't matter a whole lot whether you have a GC or not.
On the other hand, if your embedded system is a 50Mhz 8-bit microcontroller with a whooping 8KB of "RAM" (and 16KB of program memory) doing ~20k sensor reads per second, then it WILL matter.
They seem to be targeting armv7 and their reference device (Wanboard Quad) has a 1GHz clock and 2GB of ram, which is a monster compared to most embedded systems I've worked on.
That gives them plenty of cycles and memory to waste doing garbage collection, a luxury SOME of us can't afford.
Go has structs and defaults to avoiding boxed values. It also has escape analysis and tries to do stack allocation before putting objects on the heap.
Compared to Java, it is a lot easier to write your code in a way that minimises or even avoids generating garbage that has to be collected in the first place.