The golang race detector complains about some unimportant races, and as
a result, this patch adds some mutexes to prevent these test failures.
We actually lock more than necessary, because a more accurate version
would be more time consuming to implement. Secondarily, it's likely that
in the future we replace this function graph algorithm with something
that is guaranteed to be glitch-free and supports back pressure.
I noticed a very intermittent test failure where interpret would end up
running, but *fail* because a value wasn't present. This should never
happen, because the function engine is designed to only call interpret
when there has been at least one value produced for every node in the
AST. So what is the bug that would produce:
interpret error: could not interpret: func value does not yet exist
About 20 minutes ago while I was getting to bed, it occurred to me where
to look! Out of bed and to the laptop, and after briefly reminding
myself of the code, I think I've found the issue.
What I think was happening, was that an AST node would produce a value,
and send a message on the aggregate channel. This channel is monitored,
and every time it receives a message, it checks to ensure that all the
values now exist before producing a message for interpret to run.
However, this AST node was not the final one to be produced, but before
the message was read by the aggregate channel, the last remaining AST
node ran and set it's "loaded" state to `true`, but *before* its value
was made available for the aggregate channel to read. That channel then
occasionally won the race and tried to access a value before it existed,
thus causing out intermittent bug.
At least I think that's what was going on. Hopefully this patch fixes
this, if not, then there's another bug hiding too! And of course, this
entire function engine could do with some proper analysis from someone
familiar with glitches, back pressure, and FRP parallelism.
One particular note was that I used my brain, not some fancy debugging
tool to find this. Maybe skilled debuggers can fork lift their tools
onto this type of problem, but I haven't those skills!
¯\_(ツ)_/¯
This adds a simple API for adding static, polymorphic, pure functions.
This lets you define a list of type signatures and the associated
implementations to overload a particular function name. The internals of
this API then do all of the hard work of matching the available
signatures to what statically type checks, and then calling the
appropriate implementation.
While this seems as if this would only work for function polymorphism
with a finite number of possible types, while this is mostly true, it
also allows you to add the `variant` "wildcard" type into your
signatures which will allow you to match a wider set of signatures.
A canonical use case for this is the len function which can determine
the length of both lists and maps with any contained type. (Either the
type of the list elements, or the types of the map keys and values.)
When using this functionality, you must be careful to ensure that there
is only a single mapping from possible type to signature so that the
"dynamic dispatch" of the function is unique.
It is worth noting that this API won't cover functions which support an
arbitrary number of input arguments. The well-known case of this,
printf, is implemented with the more general function API which is more
complicated.
This patch also adds some necessary library improvements for comparing
types to partial types, and to types containing variants.
Lastly, this fixes a bug in the `NewType` parser which parsed certain
complex function types wrong.
I forgot to handle the special case of a function using this API that
received no inputs. It was waiting for the first input to come in, and
as a result was never producing any output.
Remember that functions like this should *almost* be thought of as
constants of the system. You would expect their output to never change
during the lifetime of a particular program invocation.
This patch adds a simple function API for writing simple, pure
functions. This should reduce the amount of boilerplate required for
most functions, and make growing a stdlib significantly easier. If you
need to build more complex, event-generating functions, or statically
polymorphic functions, then you'll still need to use the normal API for
now.
This also makes all of these pure functions available automatically
within templates. It might make sense to group these functions into
packages to make their logical organization easier, but this is a good
enough start for now.
Lastly, this added some missing pieces to our types library. You can now
use `ValueOf` to convert from a `reflect.Value` to the corresponding
`Value` in our type system, if an equivalent exists.
Unfortunately, we're severely lacking in tests for these new types
library additions, but look forward to growing some in the future!
The test for gometalinter got silently broken in an earlier commit.
Look for the missing space that was added back in this commit to see
why! In any case, this now fixes some of the things that weren't
previously caught by this change.
If anyone knows how to run these sorts of tests properly so that entire
packages are tested and so that we can enable additional tests, please
let me know!
It's also unclear why goreportcard catches a few additional problems
which aren't found by running this ourselves.
See:
https://goreportcard.com/report/github.com/purpleidea/mgmt
for more information.
This is an initial implementation of the mgmt language. It is a
declarative (immutable) functional, reactive, domain specific
programming language. It is intended to be a language that is:
* safe
* powerful
* easy to reason about
With these properties, we hope this language, and the mgmt engine will
allow you to model the real-time systems that you'd like to automate.
This also includes a number of other associated changes. Sorry for the
large size of this patch.